<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Jinmu Go&apos;s Blog</title><description>A Smooth Sea Never Made a Skilled Sailor</description><link>https://jinmugo.com/</link><item><title>구름톤 12기 프론트 개발자 참가 후기</title><link>https://jinmugo.com/posts/12th-9oormthon/</link><guid isPermaLink="true">https://jinmugo.com/posts/12th-9oormthon/</guid><description>구름톤(9oormthon) 12기 프론트 개발자의 참가 후기</description><pubDate>Fri, 20 Dec 2024 02:40:24 GMT</pubDate><content:encoded>&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;2024.12.10 ~ 2024.12.13까지 진행된 구름톤(9oormthon) 12기 프론트엔드 개발자로 참가하였습니다.
개인적인 첫 해커톤 회고에 더해, 예비 구름토너 분들께 도움이 될 수 있도록 작성하였습니다.&lt;/p&gt;
&lt;h2&gt;참여 동기&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;타 직업군과의 협업&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;IT 서비스 개발 과정에서 기획과 디자이너 분들의 결과물을 이해하고 수정을 요구하거나 의견을 더하여 아이디어를 함께 발전시키는 경험을 하고 싶었습니다.
반대로 프로그래밍 과정 중 생길 수 있는 문제들을 기획자나 디자이너 분들이 이해할 수 있도록 설명하는 경험을 하고 싶었기 때문에 참가하게 되었습니다.
처음 프로그래밍을 배운 &lt;a href=&quot;https://42seoul.kr&quot;&gt;42seoul&lt;/a&gt;에서는 주로 개발 직무에 계시거나 개발을 배우신분들과 함께 협업을 했기 때문에, 이번 구름톤에서는 개발에 대한 이해도가 적은 분들과 협업을 하여 복잡한 상황들을 쉽게 풀어내 그분들께 전하는 연습을 하고 싶었습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;빠르고 정확한 제품 개발 훈련&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;실무에서는 마감기한을 설정하고 그에 맞추어 제품을 개발해야 합니다.
구름톤에서 집중적으로 몰입하여 속도와 더불어 정확성을 겸비하는 훈련을 하기 위해 참가하게 되었습니다.&lt;/p&gt;
&lt;h2&gt;지원서 작성&lt;/h2&gt;
&lt;p&gt;지원서는 총 &lt;strong&gt;5가지 공통 항목&lt;/strong&gt;에 + &lt;strong&gt;개발 직무 관련된 1가지 항목&lt;/strong&gt;이 존재합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;구름톤 참여 동기&lt;/li&gt;
&lt;li&gt;최근 제주도가 직면한 사회 문제&lt;/li&gt;
&lt;li&gt;위의 문제를 해결하기 위한 서비스 아이디어&lt;/li&gt;
&lt;li&gt;유연한 협업을 이끌어 나가기 위한 나만의 장점.&lt;/li&gt;
&lt;li&gt;구름톤에서의 &apos;성장&apos; 목표&lt;/li&gt;
&lt;li&gt;진행한 프로젝트 설명.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위의 항목 중 &lt;strong&gt;2, 3, 6번에 조금 더 집중&lt;/strong&gt;하여 지원서를 작성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;문제 정의 + 해결 방안 제시&lt;/strong&gt;
2,3번 항목은 문제를 정확하게 진단한 이후, 해결 하는능력을 보기 위해 제시된 항목이라고 생각하였습니다.
구름톤이 12기 까지 지나올 동안 많은 서비스들이 개발되었고 많은 기획들이 나왔을 것이기 때문에, 문제 정의는 이제 새로울게 없을 것이라 생각하였습니다. 그래서 문제를 해결하기 위한 서비스 아이디어를 제시하는 데에 더 많은 시간을 할애하였습니다. 어떻게 하면 이 문제를 정확하게 해결할 수 있을지에 대한 아이디어를 제시하는 것이 더 중요하다고 생각하였기 때문입니다&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;프로젝트의 동기와 목표&lt;/strong&gt;
6번 항목은 이전에 진행한 프로젝트를 설명하는 것이었습니다.
&lt;a href=&quot;https://42seoul.kr&quot;&gt;42seoul&lt;/a&gt;에서 진행한 프로젝트들(&lt;a href=&quot;https://github.com/WebWaveMaker/webserv&quot;&gt;webserv&lt;/a&gt;, &lt;a href=&quot;https://github.com/42ForYou/ft_transcendence&quot;&gt;ft_transcendence&lt;/a&gt;등)를 소개하려 했지만, 모두 주어진 과제만을 해결한 프로젝트여서 프로젝트를 진행한 동기와 목표를 잘 설명할 수 없을 것 같았습니다. 그래서 제가 직접 느낀 불편함에서 비롯된 Obsidian의 간단한 플러그인을 만들었던 &lt;a href=&quot;https://github.com/JinMuGo/obsidian-go-up&quot;&gt;obsidian-go-up&lt;/a&gt; 프로젝트를 소개하였습니다.
최대한 왜 이 프로젝트를 진행하였는지에 대한 동기와 이를 어떻게 해결하고자 하였는지를 글에 녹여 내기위해 노력하여 작성하였습니다.&lt;/p&gt;
&lt;p&gt;제출한지 4일만에 합격메일을 받고 구름톤에 참가할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;일정&lt;/h2&gt;
&lt;p&gt;각 일차별 주요 일정들은 다음과 같습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;일차&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;주요 일정&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1일차&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;해커톤 강의, 3분 Self PR, 아이데이션 주제 발표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2일차&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2분 IDEATION, 팀 빌딩, 비어파티&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3일차&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;해커톤 진행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4일차&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;해커톤 발표, 결과 발표 및 시상&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;0일차&lt;/h3&gt;
&lt;p&gt;저는 하루 전날인 12.09일 오후 제주도에 도착하여 참가 준비를 하였습니다.&lt;/p&gt;
&lt;h3&gt;자기소개 작성&lt;/h3&gt;
&lt;p&gt;구름톤 1일차에 3분 Self PR을 진행하기 때문에 Notion에 자기소개 페이지를 작성해야했습니다.
저는 워낙 자기소개에 서툴어서 첫날 진행되는 Self PR이 제일 두려웠습니다. 학창시절부터 발표를 해야해서 자리에서 일어나기만 해도 귀와 얼굴이 빨개졌거든요. 그래서 종종 발표를 피하곤 했습니다. 하지만 발표를 못한다고 해서 피하기만 해선 남을 설득하는 것조차도 못하겠더군요. 그래서 이번 기회에 조금이라도 나아지고자 도서관에서 &quot;자기소개&quot; 라는 키워드로 검색하여 나오는 책 18초 자기소개법[^self-pr-book]을 읽고, 그 책을 바탕으로 자기소개 페이지를 작성하고 발표 준비를 하였습니다.&lt;/p&gt;
&lt;p&gt;책의 초반부에 작가는 이렇게 말합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!note] 18초 자기소개법
자기소개는 자신을 소개하는 일이 아니라 내가 관여함으로써 듣는 사람이 얻을 수 있는 미래를 소개하는 일&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;이 말을 따라서, 저의 경험을 통해 청자가 어떤 것을 얻을 수 있는지 소개하는 방식으로 작성하였습니다. 다시말해, 자기소개를 제 중심이 아닌 듣는이를 중심으로 구성하였습니다. 그래서 제 포지션을 &lt;strong&gt;구름토너들의 페이스메이커로 설정&lt;/strong&gt;하고 다른 분들께 &quot;저와 팀이 되어 협업한다면 좋을 것이다!&quot; 라는 은유적인 메시지를 전달 하기 위해 노력하였습니다.&lt;/p&gt;
&lt;p&gt;아래는 제가 작성한 자기소개페이지입니다.&lt;/p&gt;
&lt;p&gt;
이후 자기소개 페이지와 대본을 모두 작성한 이후에는 잠을 자러 갔습니다.&lt;/p&gt;
&lt;h2&gt;1일차&lt;/h2&gt;
&lt;p&gt;1일차는 해커톤 강의와 Self PR, 아이데이션 주제 발표가 있었습니다.&lt;/p&gt;
&lt;h3&gt;해커톤 강의&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;오픈소스로 신나는 해커톤&lt;/em&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;MVP개발 과정에서는 다음과 같은 점을 강조하였습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;완벽함보다 속도 중시: 완벽한 제품 개발보다는 빠르게 아이디어를 구현하는 데 더 초점을 둔다.&lt;/li&gt;
&lt;li&gt;폭넓은 관심보다는 한 곳에 집중: 적절한 특징(기능)에 집중하고 그 특징의 성공을 측정한다.&lt;/li&gt;
&lt;li&gt;폭포수 대신 민첩성: 과정과 절차에 집중하지 말고, 분업과 협업으로 속도를 우선한다.&lt;/li&gt;
&lt;li&gt;기능 추가 아닌 실용성: 최대한 많은 기능을 자랑하지 말고, 사용자에게 구체적인 가치를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;확실히 짧은 기간 안에 작은 서비스 하나를 개발하여야 하다보니. 모든 것을 고려하여 개발하기 보다는 우리가 제공하고자 하는 한 점에 집중하여 그것을 완성도 있게 제공하는 것이 중요하다는 것을 느꼈습니다.&lt;/p&gt;
&lt;p&gt;그리고 이번 해커톤에서 하지 말아야 할 것들을 알려주셨습니다. 그중 하나가 &lt;strong&gt;단지 열심히 하기&lt;/strong&gt;였습니다.
무엇인가를 만들고 구현하는데만 집중하지 않고, &lt;strong&gt;우리 팀이 왜 어떻게 무엇을 하는지&lt;/strong&gt; 맥락을 잊지 않고 진행해야 한다는 것을 강조하셨습니다. 이는 비단 해커톤 뿐만이 아니겠지요.
제가 최종적으로 이번 해커톤에서 이루고자 하는 것과 닮아 있어서 이를 가슴에 새기고 구름톤에 임하였습니다.&lt;/p&gt;
&lt;h3&gt;Self PR&lt;/h3&gt;
&lt;p&gt;강의를 듣고 점심식사를 마친 뒤, 3분 자기소개 발표를 진행하였습니다.&lt;/p&gt;
&lt;p&gt;저는 전 날 미리 준비한 대본을 외워서 발표하였습니다. 아이스브레이킹도 하고, 다른 분들의 발표를 들으며 긴장을 풀 수 있었습니다.
제주도에서 하는 해커톤이라 그런지 다들 정말 다양한 곳에서 오셨더군요. 만약, 서울에서 진행하였다면 이렇게 다양한 분들을 만날 수 없었을 것 같다는 생각이 들었습니다.
긴장이 되어서 다른 분들의 발표를 집중해서 듣지는 못하였지만, 다시 한 번 이런 기회가 생긴다면 내 발표 준비도 더 열심히 하거니와 다른 분들의 발표도 더욱 집중해서 들어보고 싶다는 생각을 했습니다.&lt;/p&gt;
&lt;p&gt;아래는 제 자기소개 전문입니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!quote]+ 자기소개
안녕하세요, 구름토너들의 페이스메이커가 되고 싶은 프론트엔드 개발자 고진무입니다.&lt;/p&gt;
&lt;p&gt;제게 이번 구름톤은 특별한 기회입니다. 다양한 분야의 전문가들과 팀을 이루어 기획부터 배포까지 함께 협업하며 배울 수 있기 때문입니다. 특히 기획자와 디자이너분들과 처음 협업하며 서로의 말을 이해하고 소통하는 경험이 정말 소중할 것 같습니다. 이런 과정을 통해 공동의 목표를 이루는 뜻깊은 시간을 만들고 싶습니다.&lt;/p&gt;
&lt;p&gt;그래서 저는 이번 구름톤에서 페이스메이커가 되려 합니다. 페이스메이커는 단순히 앞서 달리는 것이 아니라, 팀원들의 상태와 요구를 파악하고 그에 맞춰 행동하는 역할을 하는데요. 상대방을 이해하고 그에 맞추어 내 행동을 결정하는 페이스 메이커의 능력이 구름톤에서도 기능한다고 생각하였습니다.&lt;/p&gt;
&lt;p&gt;제 이름은 &apos;진정할 진&apos;에 &apos;어루만질 무&apos;를 씁니다. &apos;진정한 진&apos;은 돌림자이고 어루만질 무는 &apos;없을 무&apos;에 &apos;손수 자&apos;를 써서 &apos;없는 것을 만지는 것&apos;이라는 뜻을 담고 있으며, 이를 해석하면 마음을 어루만진다는 의미가 됩니다. 저는 프로그래머가 된 이후, 사람의 마음을 어루만지는 무언가를 만들며 살아가겠다고 생각했습니다. 이번 구름톤에서도 여러분과 멋진 결과물을 만들어가고 싶습니다. 멋진 팀을 이루고 싶다면 언제든 편하게 말을 걸어주세요. 여러분과 함께 좋은 경험을 만들어 나가길 바랍니다. 감사합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;해커톤 강의 - 해커톤에서 성장하는 방법&lt;/h3&gt;
&lt;p&gt;자기소개 이후 다른 해커톤 강의가 있었는데, 이 강의에서는 해커톤에서 성장하는 방법에 대해 이야기하였습니다. 그중 기억에 남는 것은 &lt;code&gt;DONE&lt;/code&gt;과 &lt;code&gt;PERFECT&lt;/code&gt;에 대한 이야기 였습니다.&lt;/p&gt;
&lt;p&gt;우리는 대부분 &lt;strong&gt;완료(DONE)를 하였다고 하면 완벽(PERFECT)하지 않았다고 생각하고, 완벽하였다고 하면 완료하지 않았다고 인식하는 경향&lt;/strong&gt;이 있다고 합니다.
해커톤에서는 모두가 서로 다른 DONE의 기준을 가지고 있습니다. 그렇기 때문에, DONE과 PERFECT는 양극단에 있는 것이 아닙니다.&lt;/p&gt;
&lt;p&gt;그리고 DONE과 PERFECT사이에 GROWTH가 있다. 현재 DONE과 PERFECT사이에 있는 나의 상태에서 PERFECT에 가까워 지는 것이 성장이라고 합니다.&lt;/p&gt;
&lt;p&gt;그래서 &quot;PERFECTLY DONE.&quot; 해커톤의 매순간. 완벽에 가깝게 완료하는 것을 목표로 하고 진행하라 하셨습니다.&lt;/p&gt;
&lt;p&gt;이 강의는 칙센트 미하이의 &amp;lt;몰입&amp;gt;&lt;a href=&quot;%5B%EB%AA%B0%EC%9E%85%5D(https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=503603)&quot;&gt;^1&lt;/a&gt;을 기반으로 작성되었다고 합니다. 관심이 있으신 분들은 한번쯤 읽어 보셔도 좋을 것 같습니다.&lt;/p&gt;
&lt;h3&gt;아이데이션 주제 발표&lt;/h3&gt;
&lt;p&gt;그리고 1일차의 마지막에 아이데이션 주제 발표가 있었습니다.
이번 12기의 주제는 &lt;strong&gt;고령화&lt;/strong&gt;였습니다.&lt;/p&gt;
&lt;p&gt;저는 지원서 작성할 때, 고령화와 관련된 서비스를 제시하여서 이를 조금 더 고도화 하여 PPT를 만들었습니다.&lt;/p&gt;
&lt;h3&gt;1일차 마무리&lt;/h3&gt;
&lt;p&gt;1일차에는 첫날 같은 자리에 앉은 팀원들과 중간중간 나가서 산책도하고 저녁에는 다른 구름토너분들과 흑돼지를 먹으며 즐겁게 시간을 보냈습니다. 구름톤 진행하면서 밖에 나갈일이 그렇게 많지 않습니다. 1일차 부터 많이 돌아다니시는 것을 추천드려요. 제주도에 와있는게 가끔 실감이 나지 않을 때가 있습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;2일차&lt;/h2&gt;
&lt;p&gt;2일차에는 전날 발표한 아이데이션 주제인 &apos;고령화&apos;에 대해 각자 아이디어를 생각한 이후 발표하는
2분 IDEATION시간과 팀 빌딩, 비어 파티&lt;/p&gt;
&lt;h3&gt;2분 IDEATION&lt;/h3&gt;
&lt;p&gt;2분동안 PPT 한 페이지로 아이디어를 발표 하는 시간입니다.
저는 &apos;길벗&apos;이라는 서비스를 제안하였고. &lt;strong&gt;1분 문제정의&lt;/strong&gt; &lt;strong&gt;1분 해결방안&lt;/strong&gt;을 큰 틀로 잡고 발표하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;발표 시간이 짧기도 했고 PPT가 1장뿐이어서 어제보다는 긴장을 덜한채로 발표할 수 있었습니다.
그리고 나서는 다른분들의 발표를 들으며 나는 어떤 사람과 어떤 아이디어로 팀을 이루고 싶은지 생각하였습니다.&lt;/p&gt;
&lt;h3&gt;팀빌딩&lt;/h3&gt;
&lt;p&gt;팀빌딩 시간은 시장에 온듯한 분위기였습니다. 아무런 시스템 없이 각자 원하는 팀원에게 의견을 묻고 팀을 결성하는 방식이었습니다.&lt;/p&gt;
&lt;p&gt;저는 아이데이션을 들으며 함께 하고 싶은 사람들을 미리 리스트업 해놓았고, 운이 좋아서 다행히 팀을 구성하는데 큰 어려움은 없었습니다. 만약 구름톤에 참가하게 된다면, 팀빌딩 시간에는 자신이 어떤 사람과 어떤 아이디어로 팀을 이루고 싶은지 미리 생각해두는 것이 좋을 것 같습니다.
그리고 이런 시간은 어쩔 수 없이 &apos;운&apos;이 작용하는 것 같습니다. 이번 해커톤은 처음에 랜덤으로 배정된 자리 기준으로 비교적 가까이에 있는 사람들과 팀을 이루게 되는 경우가 많았다.&lt;/p&gt;
&lt;p&gt;그러니 원하는 목표나 결과가 있다면 그것을 위해 적극적으로 움직이는 것이 중요합니다. 팀원이 다가오기를 바라는 것보다 직접 움직여서 팀을 이루는 것이 본인에게 더 만족스러운 결과를 가져올 것입니다.&lt;/p&gt;
&lt;p&gt;그렇게 해서 우리 팀 &quot;&lt;a href=&quot;https://github.com/ddol-mang&quot;&gt;&lt;strong&gt;올망졸망똘망할망&lt;/strong&gt;&lt;/a&gt;&quot;이 결성되었습니다. 기획자 분이 번뜩! 생각해내신 팀명이 순식간에 모두의 마음에 들어 팀명으로 결정하게 되었습니다.&lt;/p&gt;
&lt;p&gt;할망을 위해 올망졸망 모인 똘망이들. 뜻도 좋지 않나요?&lt;/p&gt;
&lt;p&gt;팀이 결성된 이후 &apos;제주도&apos;, &apos;클라우드&apos;, &apos;고령화&apos;에 맞는 주제를 선정하기 위해 브레인스토밍 시간을 가졌습니다. 하지만, 주제에 맞는 서비스를 선정하는 것이 쉽지 않았습니다.
고령화라는 사회적 문제를 근본적으로 해결할 수는 없었고, 결국 고령화로 인해 발생하는 문제를 완화하거나 해결할 수 있는 서비스를 개발하는 방향으로 아이데이션을 진행하였습니다.&lt;/p&gt;
&lt;p&gt;하지만, 결국 저녁 6시까지 마땅한 아이디어가 나오지 않은 채로 비어파티에 참여하게 되었습니다.&lt;/p&gt;
&lt;h3&gt;비어파티&lt;/h3&gt;
&lt;p&gt;결성된 팀원 외에 다른 팀원들과 멘토님들과 소통할 수 있는 비어파티 시간이 있었습니다.&lt;/p&gt;
&lt;p&gt;비어파티는 세가지 타임 테이블을 기반으로 진행되었습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;랜덤으로 지정된 좌석에 앉아 있는 사람들과 대화를 나누는 시간 (6시 ~ 7시 30분)&lt;/li&gt;
&lt;li&gt;직무별로 나누어진 테이블에서 자유롭게 대화를 나누는 시간 - 멘토님 참여 (7시 30분 ~ 9시)&lt;/li&gt;
&lt;li&gt;자유롭게 다른 팀원들과 대화를 나누는 시간 (9시 ~ 11시)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이번 해커톤에서 손에 꼽게 즐거웠던 시간이었습니다. 구름톤에 오신 다른 분들과 대화를 나누는 것이 즐거웠고, 그분들의 이야기를 들으면서 다양한 생각을 할 수 있었습니다.
구름톤에 참가하시는 구름토너 분들은 비어파티에서 만큼은 다른 분들과 이야기를 충분히 하고 네트워킹 하는 것이 구름톤을 200% 즐기는 방법이라 생각합니다.&lt;/p&gt;
&lt;h3&gt;주제 결정&lt;/h3&gt;
&lt;p&gt;비어파티 이후에 다시 모여 팀원들과 주제를 결정하는 시간을 가졌습니다. 내일 본격적으로 해커톤에 들어가기 앞서 반드시 주제를 선정하여야 했습니다.
굉장히 많은 이야기를 했던 것 같네요. 해녀분들을 위한 서비스. 노인과 청년의 연결. 템플스테이같은 할머니, 할아버지의 집을 이용한 서비스 등등 많은 주제들이 오갔지만 이런저런 이유들 때문에 기각되다.
한 팀원분이 발견하신 해커톤을 진행 하던 당일에 발간된 &lt;a href=&quot;https://www.jejunews.com/news/articleView.html?idxno=2215293&quot;&gt;한 신문기사&lt;/a&gt;를 보고 주제를 선정하게 되었습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!quote]
즉 노인의 행복도에서는 자녀들은 건강하다, 자녀들이 결혼해서 화목하게 살고 있다, 몸이 허락하는 한 활동을 계속한다 등이 선두를 달린다.&lt;/p&gt;
&lt;p&gt;... (중략)&lt;/p&gt;
&lt;p&gt;노인의 삶의 만족도에서는 &quot;&lt;strong&gt;자녀와의 관계&lt;/strong&gt;&quot;가 생활 수준이나 현재의 삶보다 더 큰 영향을 미친다.
요컨대 제주지역 노인들은 행복의 중심에 자녀를 두고 있으되 의존적이지 않고 관계 지향적이며, 일을 통해 생활의 독립과 삶의 의미를 탐색하고 자녀의 건강과 행복을 자신보다 더 소중하게 여기는 특성을 나타낸다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;위의 기사를 통해 우리는 &lt;strong&gt;제주 부모와 육지 자식 간 연결이 필요&lt;/strong&gt;하다는 문제인식을 하게되었고 이를 해결하기 위해 단순한 상호작용으로 자신의 현재 상태를 알릴 수 있는 부모-자식 간의 플랫폼을 서비스를 개발하기로 하였습니다.&lt;/p&gt;
&lt;p&gt;팀원 모두가 이 문제에 대해 공감하고 흥미를 느껴 주제를 선정하게 되었습니다. 개인적으로 처음 주제가 결정되었을 때 감격에 겨웠습니다.
새벽 4시까지 아이디어 회의를 하고 구체화하는 과정이 힘들었지만, 너무 즐거워서 시간 가는 줄 모르고 회의하였습니다.
잠이 많고 밤을 잘 못새는 편이라 해커톤 시작 전에 걱정하였는데. 그런걱정이 무색할 정도로 몰입하였습니다.&lt;/p&gt;
&lt;p&gt;이후 내일 진행될 해커톤을 위해 방에 들어가 잠을 자기로 하였습니다. 늦잠을 잘까봐 다들 불안해서 팀원들과 각자의 호실을 카카오톡에 공유하고, 잠자리에 들었습니다.&lt;/p&gt;
&lt;h2&gt;3일차&lt;/h2&gt;
&lt;p&gt;주제가 선정된 이후에는 구체적인 서비스를 개발하기 위한 회의를 하며 API, DB명세를 하였고 디자이너님의 화면 설계에 따라 프론트엔드 개발 계획을 세웠습니다.&lt;/p&gt;
&lt;p&gt;아직 react-query나 zustand와 같은 library에 익숙지 않아서 다른 프론트 개발자분에게 화면 구현 + 백엔드 API를 중점적으로 부탁드려 개발을 진행하였고 저는 화면 구현 + 프론트엔드 단의 배포 환경 설정을 담당하였습니다.&lt;/p&gt;
&lt;p&gt;그렇게 개발을 진행하던 중 배포된 우리 서비스가 &lt;strong&gt;몇초 간격으로 계속 리렌더링이 발생&lt;/strong&gt;하는 것을 확인하였습니다. 새벽 4시쯤 문제를 발견하고 한 2시간동안 문제를 붙들고 있던 와중 우리 서비스가 개발 모드로 실행되어 있는 것을 발견하였죠.
저는 이 부분이 문제라고 생각하여 프로덕션 모드로 변경하길 원했지만, 크램폴린(배포 환경)에 대한 이해도가 없어 멘토님들에게 도움을 요청하였습니다.
제 의도는 간단하였습니다. 서비스는 vite로 개발되었고, 원래 개발모드로 실행되고 있던 서비스를 프로덕션 모드로 실행되게 변경하고 싶었습니다. 사건의 경위는 아래와 같았습니다.&lt;/p&gt;
&lt;p&gt;백엔드 개발자 분이 크램폴린이 익숙지 않아 배포를 하는 것이 어려워 우리가 원래 하려고 했던 nginx를 이용한 정적 서버를 띄우는 방법이 아닌 다른 방법을 찾아야 했습니다.
그래서 백엔드 분이 멘토님께 도움을 요청하였고, 그 과정에서 기존 정적 빌드 이후에 nginx로 정적 파일을 서빙하는 로직이 아닌 개발 모드로(e.g &lt;code&gt;pnpm dev&lt;/code&gt;) frontend 코드를 띄우게 된 것이었습니다.
vite에는 프로덕션 환경으로 정적 파일을 서빙해주는 &lt;code&gt;images&lt;/code&gt;커맨드가 있습니다. &lt;code&gt;package.json&lt;/code&gt; &lt;code&gt;script&lt;/code&gt;에 &lt;code&gt;images&lt;/code&gt;가 아닌 &lt;code&gt;start&lt;/code&gt;와 같은 더욱 직관적인 프로덕션 환경으로 실행되는 스크립트를 추가하였다면 이런 일이 발생하지 않았을 것이라는 생각이 들더군요. 추상화의 중요성을 다시금 느낄 수 있던 시간이었습니다.&lt;/p&gt;
&lt;p&gt;그동안 기획자와 디자이너분들은 발표 준비를 하고 계셨습니다. 발표 준비 과정에 많이 참여하지 못하여서 기획자분과 디자이너분께 죄송한 마음이었습니다.
다음날 10시가 되어서야 발표준비와 개발이 마무리되었습니다.&lt;/p&gt;
&lt;p&gt;해커톤을 진행하던 와중에 갔던 성산일출봉.
그리고 팀원들과 함께 찍은 사진.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;4일차&lt;/h2&gt;
&lt;p&gt;4일차에는 해커톤 발표 및 시상이 있었습니다.&lt;/p&gt;
&lt;h3&gt;해커톤 발표&lt;/h3&gt;
&lt;p&gt;그렇게 밤을 새가며 준비한 서비스 &quot;&lt;a href=&quot;https://github.com/ddol-mang/hey-oldlady&quot;&gt;어이, 할망&lt;/a&gt;&quot;을 발표하였습니다.&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;strong&gt;고령화&lt;/strong&gt;라는 키워드에 집중하고 이를 해결하기 위해 간편한 사용자 흐름에 집중한 것이 굉장히 즐거운 경험이었습니다.&lt;/p&gt;
&lt;h2&gt;Outro&lt;/h2&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;[^self-pr-book]: &lt;a href=&quot;https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=94177413&quot;&gt;18초 자기소개법&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>구름톤</category><category>구름톤 12기</category><category>해커톤</category><author>Jinmu Go</author></item><item><title>Amazon Q Developer CLI 간단 체험기</title><link>https://jinmugo.com/posts/amazon-q-developer-cli/</link><guid isPermaLink="true">https://jinmugo.com/posts/amazon-q-developer-cli/</guid><description>Amazon Q Developer CLI를 설치하고 기획 문서를 컨텍스트로 제공해 Pygame 게임을 만든 과정과 사용해 본 소감을 정리했습니다.</description><pubDate>Wed, 04 Jun 2025 13:25:20 GMT</pubDate><content:encoded>&lt;p&gt;AWSKRUG에서 Amazon Q Developer로 간단한 게임 만드는 이벤트를 한다고 알려줘서 참여해보았습니다.
&lt;/p&gt;
&lt;p&gt;참여 방법은 간단합니다.&lt;/p&gt;
&lt;p&gt;Amazon Q CLI로 게임을 만들고 어떻게 만들었는지 블로그를 쓰거나 비디오를 만들어서 관련 링크를 Form에 제출하면 됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!info] 참여방법&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;STEP 1&lt;/strong&gt; : Sign up to an AWS Builder ID and claim your unique community.aws username from &lt;a href=&quot;https://community.aws/builderid?trk=b085178b-f0cb-447b-b32d-bd0641720467&amp;amp;sc_channel=el&quot;&gt;this link.&lt;/a&gt; If you need help or network with other fellow builders, join the discord &lt;a href=&quot;https://discord.gg/KNC9JQAfKT&quot;&gt;server from this link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;STEP 2&lt;/strong&gt; : &lt;a href=&quot;https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-installing.html&quot;&gt;Install Amazon Q CLI&lt;/a&gt; in your machine. Here are two guides from Ricardo Sueiras on how you can install in &lt;a href=&quot;https://community.aws/content/2ulGwNwLFj5grS8hXJBMCN78Qwl/the-essential-guide-to-installing-amazon-q-developer-cli-on-linux?trk=6f6cb092-f1ba-456b-8644-73ed7ccbd567&amp;amp;sc_channel=el_&quot;&gt;Linux&lt;/a&gt; and in &lt;a href=&quot;https://community.aws/content/2v5PptEEYT2y0lRmZbFQtECA66M/the-essential-guide-to-installing-amazon-q-developer-cli-on-windows?trk=e07eca93-fa2f-4351-b567-f293b83eb635&amp;amp;sc_channel=el_&quot;&gt;Windows&lt;/a&gt;. Install &lt;a href=&quot;https://www.pygame.org/wiki/GettingStarted&quot;&gt;PyGame&lt;/a&gt; or some other gaming library on your laptop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;STEP 3&lt;/strong&gt; :Start a chat session with Amazon Q CLI and build a game with just prompts in the chat. Try to be innovative as possible with your game so you explore the possibilities of Amazon Q CLI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;STEP 4&lt;/strong&gt; : Write a blog about what you’ve built or create a video about it. Make a public post in a social media of your liking with the hashtag #AmazonQCLI. It can be in your local language, which we encourage. Optionally you can also host your code in a &lt;a href=&quot;https://github.com/&quot;&gt;github&lt;/a&gt; repository.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;STEP 5&lt;/strong&gt; : &lt;a href=&quot;https://pulse.aws/survey/ZO9G4AEL&quot;&gt;Fill this T shirt redemption form&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;게임을 만들어야 하니 우선 Amazon Q Develop CLI를 설치해야합니다.
기본적으로 macOS과 Linux 기반 OS에서만 설치할 수 있는 것으로 보이고. Window는 WSL를 이용해야합니다. 자세한 설치방법은 아래링크에서 확인할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;Installing Amazon Q for command line - Amazon Q Developer&quot;
image: &quot;https://docs.aws.amazon.com/assets/r/images/aws_logo_light.svg&quot;
description: &quot;Learn how to install Amazon Q Developer for command line on different operating systems including macOS, Linux, and Windows Subsystem for Linux (WSL).&quot;
url: &quot;https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-installing.html&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치 하고 terminal에서 &lt;code&gt;q&lt;/code&gt;를 입력한후 아래와 같은 화면이 나온다면 성공입니다!
&lt;/p&gt;
&lt;h2&gt;Time To Create!&lt;/h2&gt;
&lt;p&gt;설치를 완료하고 나서 게임을 만들어야 하는데 저는 Python을 자주 사용한 것도 아니고 Pygame에 대해서도 몰라서 정말 &lt;code&gt;q&lt;/code&gt;에게 모든 코딩로직과 이미지 생성등을 맡겼습니다.&lt;/p&gt;
&lt;p&gt;이전에 학교 수업에서 게임을 기획한 기획 문서가 있었는데 이걸 gpt한테 주고 영어 markdown으로 재생성해서 aws q에게 주었습니다.&lt;/p&gt;
&lt;p&gt;Elysian-Arcana라는 이름의 게임 기획이었고 이걸 어떻게 주나 싶어서 amazon q 의 &lt;code&gt;/help&lt;/code&gt;기능이 있어 살펴보았더니 &lt;code&gt;context&lt;/code&gt;라는 명령이 있어서 context를 제공할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;그래서 q의 context에 제가 기획한 게임 기획 문서를 넣고 이 markdown문서를 기반으로 미니 게임을 만들어달라고 했습니다.&lt;/p&gt;
&lt;p&gt;로그라이크 기반의 턴제 카드 게임을 만들어 주었네요.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;나름 잘 만들어주었습니다. 그래도 돌아가는 게임을 AI가 만든다는게 정말 신기하네요. 디테일한 수정사항이나 게임성외 많은 것들은 여전히 부족하지만 그래도 정말 바이브 코딩으로만 만든 결과물이 이정도 라는게 신기합니다.&lt;/p&gt;
&lt;p&gt;
그래도 나름 게임성을 챙기기 위해 한 스테이지를 클리어하면 카드를 고를 수 있게 만들어 주네요.&lt;/p&gt;
&lt;p&gt;오 스테이지가 지나니 제 캐릭터들이 레벨업을 하네요. 마나통도 늘어나고 체력도 늘어났습니다. 그리고 이제보니 상대 몬스터가 어떤 공격을 하는지 intent도 보여줍니다. 나름 게임편의성을 챙긴 모습도 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;이렇게 게임을 만들 수 있는건 신기하긴 하지만 아쉬운 부분도 있었습니다.
상대 몬스터의 그림을 계속해서 만들고 연결해달라고 3번 정도 요청을 했었는데 제가 prompt를 잘 작성하지 못한 탓인지 계속만들어 주지 못하더라고요 그 부분이랑 모든 카드들의 이미지도 똑같았습니다.&lt;/p&gt;
&lt;p&gt;하지만 이정도로 발전이 된게 놀라웠습니다. 정말 저는 구현에 대한 세부사항은 아무것도 모른채 기능과 화면의 정의 같은 것들만 계속해서 요청하며 이런 수준까지 만들어 주는 것이 신기했습니다.&lt;/p&gt;
&lt;p&gt;무료로 사용할 수 있으니 관심이 생기신다면 한번 참여해 보시는 것도 좋을 것 같습니다. 소스 코드는 아래에 첨부 해놓겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;GitHub - JinmuGo/elysian_arcana_aws_q: build game with aws q&quot;
image: &quot;https://opengraph.githubassets.com/d0cf93f2488e20f0c8ccd3df91300d029b5f4bfc5f2e02ab63a5acd186bd0c03/JinmuGo/elysian_arcana_aws_q&quot;
description: &quot;build game with aws q. Contribute to JinmuGo/elysian_arcana_aws_q development by creating an account on GitHub.&quot;
url: &quot;https://github.com/JinmuGo/elysian_arcana_aws_q&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><category>AmazonQCLI</category><category>AWS</category><author>Jinmu Go</author></item><item><title>[CLF-C02] AWS Cloud Practitioner 자격증 취득 후기</title><link>https://jinmugo.com/posts/aws-clf-c02/</link><guid isPermaLink="true">https://jinmugo.com/posts/aws-clf-c02/</guid><description>AWS Cloud Practitioner CLF-C02를 준비한 순서, 공식 학습 자료와 모의고사 활용법, 온라인 감독 시험 당일 경험을 공유합니다.</description><pubDate>Thu, 22 May 2025 00:03:25 GMT</pubDate><content:encoded>&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;최근 클라우드를 공부하고 있습니다. 자격증이 온전히 실력을 대변해 주지는 못하지만, 어느 정도 성과를 보면서 공부하고 싶어 그 첫 단계로 AWS Cloud Practitioner 자격증을 취득했습니다. 이 글에서는 제가 이 자격증을 준비하면서 경험한 내용과 효율적인 공부 방법 및 팁을 공유하고자 합니다.&lt;/p&gt;
&lt;h2&gt;공부 방법 및 준비 과정&lt;/h2&gt;
&lt;p&gt;총 준비 기간은 2개월이었지만, 여러 가지 일이 많아서 제대로 공부하지 못했습니다. 시험 보기 1주 전부터 부랴부랴 공부했던 것 같습니다.&lt;/p&gt;
&lt;h3&gt;준비 순서&lt;/h3&gt;
&lt;h4&gt;&lt;a href=&quot;https://explore.skillbuilder.aws/learn/courses/13522/aws-cloud-practitioner-essentials-hangug-eo&quot;&gt;AWS 공식 학습 자료 (AWS Skill Builder)&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;AWS에서 Cloud Practitioner를 위한 강좌를 만들었는데, 이보다 좋은 강의는 없다고 생각합니다. 처음 클라우드를 배우는 사람이나 클라우드에 익숙한 분들도 이해하기 쉽게 만들어져있습니다. 처음 클라우드에 입문하려는 분들은 이 강좌를 꼭 시청하시길 바랍니다.&lt;/p&gt;
&lt;p&gt;각 Module이 끝나면 해당 개념과 관련된 백서 링크를 제공해 주는데, 그 링크를 이용해서 모듈별로 공부하시면 많은 도움이 됩니다.&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://explore.skillbuilder.aws/learn/courses/16814/aws-certified-cloud-practitioner-official-practice-question-set-clf-c02-hangug-eo/lessons&quot;&gt;AWS Certified Cloud Practitioner Official Practice Question Set (CLF-C02 - 한국어)&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;그리고는 20개 문항의 간단한 문제들을 푸시면 좋습니다. AWS에서 만든 것이라 문제 설명도 잘 나와 있고 내가 어느 문항에서 시간을 오래 쓰는지 어떤 개념이 부족한지 한눈에 파악할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.udemy.com/course/aws-certified-cloud-practitioner-new/&quot;&gt;Udemy의 Stephane Maarek 강의&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;udemy에서 가장 유명한 Cloud Practitioner 강의입니다. 위의 두 과정을 지나면서 어떤 개념이 부족한지 파악한 다음에 해당 부분만 들으셔도 좋고. 시간이 되신다면 처음부터 끝까지 들으셔도 좋습니다. AWS 공식 학습 자료보다 조금 더 상세히 공부하실 수 있습니다. 그리고 Section 22에 있는 모의고사를 꼭 풀어보시는 것을 추천해 드립니다. 저는 시험 보기 전까지 이 모의고사만 5번 풀었네요. 여기에 나오는 문제들과 관련된 개념과 사용법들을 익히시면 거뜬히 통과하시리라 생각됩니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h4&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=T17iGqvYi6s&quot;&gt;AWS 강의실 Youtube&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;만약 처음 AWS 자격증 시험을 보신다면 위의 영상을 시청하는 것을 추천해 드립니다. 시험 관련이나 자격증에 대한 정보들을 많이 알아갈 수 있습니다.&lt;/p&gt;
&lt;h2&gt;시험 당일 (온라인 시험)&lt;/h2&gt;
&lt;p&gt;시험은 온라인 감독 시험으로 집에서 진행했습니다. 아무래도 집에서 보는 것이라 이것저것 걱정이 많았습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;신분증 (여권) 준비&lt;/li&gt;
&lt;li&gt;깨끗한 책상 환경 유지&lt;/li&gt;
&lt;li&gt;주변 환경을 감독관에게 카메라로 보여줘야 하므로, 미리 준비하고 정리&lt;/li&gt;
&lt;li&gt;인터넷 연결 상태 점검 (안정적인 네트워크 환경 필수)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;책상에 텀블러를 두었는데 딱히 제재하진 않았습니다. 다른 후기에서는 감독관이 여러 가지를 시킨다고 했었는데 저는 책상 위 보여달라 -&amp;gt; 손목 보여달라 -&amp;gt; 휴대폰 손에 닿지 않는 곳에 두어라. 한 다음에 바로 시험에 들어갔습니다.&lt;/p&gt;
&lt;p&gt;인도인 감독관이 들어왔었는데 억양이 알아듣기 힘들었지만, 감독관이 잘 못 알아듣는 것 같으면 채팅으로 문장을 적어주셔서 그래도 진행할 수 있었습니다.&lt;/p&gt;
&lt;h2&gt;시험 팁&lt;/h2&gt;
&lt;p&gt;시험은 총 90분 + (한국어 관련 설정 30분)을 주고. 문항은 65문항이다. 시험에 들어가기 전 내가 몇 개 정도를 틀려야 합격할 수 있을지에 대한 생각을 하고 들어가는 게 좋습니다. 예를 들어 CLF-C02 시험 같은 경우는 700점이 합격 점수이고 65문항 중에 약 45문제 정도 맞추어야 합니다. 그러면 시험 중에 확실하게 정답을 확신할 수 없는 것이 20개를 넘지 않아야 한다는 의미가 됩니다. 이렇게 미리 인지하고 들어가야 조금 더 확신을 가지고 시험에 임할 수 있습니다.&lt;/p&gt;
&lt;p&gt;문제를 모두 푼 이후 50분이 지났고
몇 차례의 검토하고 나니 20분이 지나고 8문제에 flag가 꽂혀있었고 시험을 마무리했습니다.
시험 결과는 바로 나오고 자세한 결과는 추후 메일로 알려줍니다.&lt;/p&gt;
&lt;h2&gt;Outro&lt;/h2&gt;
&lt;p&gt;저는 10(5/21일)시에 시험을 보고 11시 10분에 끝냈고 credly에서 20시에 메일로 알려주고 다음 날 6시(5/22일)에 AWS에서 세부 정보를 알려주는 메일이 와있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;비교적 짧은 시간이었지만, 자격증을 준비하면서 클라우드에 대한 애매한 지식을 확실히 정리할 수 있었습니다. 이번 경험을 바탕으로 바로 AWS Certified DevOps Engineer - Professiona 에 도전해 보려합니다.
클라우드 공부를 시작하거나 자격증 시험을 준비하시는 분들에게 이 글이 작은 도움이 되었으면 좋겠습니다.&lt;/p&gt;
</content:encoded><category>AWS</category><category>CLF-C02</category><author>Jinmu Go</author></item><item><title>블로그 댓글기능 적용기 With Fuma Comment</title><link>https://jinmugo.com/posts/blog-comment-feautre-with-fuma-comment/</link><guid isPermaLink="true">https://jinmugo.com/posts/blog-comment-feautre-with-fuma-comment/</guid><description>불완전한 댓글 기능, Fuma Comment로 깔끔하게 리빌드한 후기</description><pubDate>Thu, 01 May 2025 01:46:46 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;[!attention]
해당 글은 &lt;a href=&quot;https://fuma-comment.vercel.app/docs&quot;&gt;fuma-comment Docs&lt;/a&gt;과 &lt;a href=&quot;https://github.com/fuma-nama/fuma-comment&quot;&gt;repository&lt;/a&gt;를 참고 했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;github follow하고 있던 &lt;a href=&quot;https://github.com/velopert&quot;&gt;velopert&lt;/a&gt;님이 한 프로젝트를 starred 한 것을 우연히 보았습니다.
&lt;/p&gt;
&lt;p&gt;fuma-nama님의 &lt;a href=&quot;https://github.com/fuma-nama/fumadocs&quot;&gt;fumadocs&lt;/a&gt;라는 프로젝트였습니다. next.js 기반 document framework인데 디자인도 유려하고 무엇보다 정말 간단하게 document를 만들 수 있더군요. 놀라웠습니다. 그렇게 이 분에 대해 염탐(?)을 하던 도중 다른 프로젝트가 눈에 들어왔습니다.
&lt;/p&gt;
&lt;p&gt;바로 &lt;a href=&quot;https://github.com/fuma-nama/fuma-comment&quot;&gt;fuma-comment&lt;/a&gt;라는 프로젝트였는데요. 설명을 보니 아래와 같이 적혀 있었습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;user friendly, beautiful comment area to your blog&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;블록에 댓글 기능을 추가할 수 있는 프로젝트였죠. 저는 이거다! 싶었습니다. 왜냐하면 최근 제 블로그 댓글 기능에 고민이 많았거든요...&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;처음 댓글 기능을 만들 때는 &lt;strong&gt;불편한 로그인 기능 없이 누구든 와서 댓글을 가면 좋겠다.&lt;/strong&gt; 라는 생각으로 최대한 OAuth, ID/PW, email, magic link등등을 제거해서 오직 &lt;strong&gt;IP&lt;/strong&gt;로만 자신의 댓글을 CRUD할 수 있게 구현했습니다. 하지만 이 방법은 문제가 많았습니다. 😞&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;공공장소와 같은 IP가 공유되는 상황에서는 다른 사람의 댓글을 조작할 수 있었습니다.&lt;/li&gt;
&lt;li&gt;한 사람의 IP는 고정되지 않는다. 어제 쓴 댓글인데 오늘은 수정/삭제 버튼이 안 보이는 상황이 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;스푸핑/프록시로 악용 가능합니다. VPN/프록시로 해당 IP를 정말 간단하게 훔칠 수도 있습니다.&lt;/li&gt;
&lt;li&gt;알림/응답이 불가능합니다. IP만으로는 사용자를 식별할 수 없을 뿐더러 이메일주소를 알지도 못합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 적고나니 왜 IP로만 구현했나 싶네요. 돌아보니 누구든 제 글을 보고 편하게 댓글을 남기고 갔으면 하는 마음이 더 커서 이렇게 구현한 것 같습니다. 그것이 비록 너무 쉬워서 문제였지만요. giscus도 도입을 했었지만, theme custom이 어렵고 github 아이디가 있어야 사용가능하다는점이 아쉬웠습니다.&lt;/p&gt;
&lt;p&gt;그래서 위 프로젝트를 보고 난 뒤에 마음이 동했습니다. next-auth, better-auth, clerk auth등 다양한 Authenticator를 꽂아 사용할 수 있고 Mention기능이나 답글, 알림 기능등 다양하게 Custom가능했습니다.&lt;/p&gt;
&lt;p&gt;아래 링크를 통해 직접 프로젝트를 테스트하거나, 댓글을 남길 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;Fuma Comment&quot;
image: &quot;https://avatars.githubusercontent.com/u/104714237?v=4&quot;
description: &quot;A React.js library for adding comment area to anywhere, with your own backend &amp;amp; auth!&quot;
url: &quot;https://fuma-comment.vercel.app/&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그래서 저는 이 프로젝트를 제 블로그에 적용하기로 했습니다. 우선 지금은 간단하게 Github OAuth로 구현한뒤 나중에 magic link등으로 변환하거나 다른 방법을 생각해보려 합니다.&lt;/p&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;client: &lt;strong&gt;nextjs 15&lt;/strong&gt; with  &lt;strong&gt;Route Handler&lt;/strong&gt;
database: &lt;strong&gt;postgres&lt;/strong&gt;
ORM: &lt;strong&gt;prisma&lt;/strong&gt;
ObjectStorageService: &lt;strong&gt;uploadthing&lt;/strong&gt; (S3 기반)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
 &lt;strong&gt;Route Handler&lt;/strong&gt;를 사용하여 매번 새로운 요청들을 처리하기 때문에 &lt;code&gt;next.config.mjs&lt;/code&gt;에 &lt;code&gt;export: output&lt;/code&gt;옵션과 함께 사용할 수 없습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Library&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;@fuma-comment/react&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@fuma-comment/server&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@prisma/client&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prisma&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uploadthing&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Signin Flow&lt;/h3&gt;
&lt;p&gt;
Inspired From: https://medium.com/@enes9103/building-apis-and-handling-authentication-with-next-js-9d4a8b448251&lt;/p&gt;
&lt;h3&gt;Comment Flow&lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;#db&quot;&gt;DB&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Prisma&lt;/li&gt;
&lt;li&gt;Postgres DB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#nextjs-route-handler&quot;&gt;NextJS Route Handler&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;NextAuth&lt;/li&gt;
&lt;li&gt;uploadthing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#nextjs-client&quot;&gt;NextJS Client&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;순서로 구현 설명하겠습니다.&lt;/p&gt;
&lt;h2&gt;DB&lt;/h2&gt;
&lt;p&gt;postgresDB 하나를 준비합니다. vercel에 있는 DB Provider를 사용하셔도 되고 서버에 직접 설치 또는 docker를 사용하셔서 띄우셔도 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;저는 Coolify를 사용하고 있어서. 원격 서버에 docker를 통해 container를 띄웠습니다.
&lt;/p&gt;
&lt;p&gt;그리고 이 DB를 원격에서 접근할 수 있도록 public 설정을 해줍니다. 방화벽의 포트를 허용하고, docker라면 port binding을 진행합니다.&lt;/p&gt;
&lt;p&gt;이렇게 DB를 노출하는데에 성공했다면 &lt;code&gt;psql&lt;/code&gt;같은 client tool로 db접근을 시도합니다. postgres의 URI는 아래와 같습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;postgres://{POSTGRES_USERNAME}:{SERVICE_PASSWORD_POSTGRES}@{YOUR_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;이제 이 postgres의 public URI를 통해 Route Handler에서 접근할 것입니다.
그전에 해야할 것이 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;prisma/schema.prisma&lt;/code&gt;경로에 링크로 이동한 뒤 코드를 붙여 넣습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;fuma-comment/apps/docs/prisma/schema.prisma at main · fuma-nama/fuma-comment&quot;
image: &quot;https://repository-images.githubusercontent.com/717080536/f375c8ab-da9a-4716-973e-92b47f38244a&quot;
description: &quot;user friendly, beautiful comment area to your blog - fuma-nama/fuma-comment&quot;
url: &quot;https://github.com/fuma-nama/fuma-comment/blob/main/apps/docs/prisma/schema.prisma&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;DATABASE_URL&lt;/code&gt;&lt;/strong&gt; 환경 변수에 postgres URI를 바인딩하고 &lt;strong&gt;&lt;code&gt;prisma db push&lt;/code&gt;&lt;/strong&gt; 명령어를 통해 Prisma 스키마 파일의 현재 상태를 데이터베이스에 바로 반영합니다.&lt;/p&gt;
&lt;h3&gt;NextJS Route Handler&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/api&lt;/code&gt; 폴더 구조는 아래와 같습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/api
├── /auth
│   └── [...nextauth]
│       ├── options.ts
│       └── route.ts
├── /comments
│   └── [...comment]
│       └── route.ts
├── uploadthing
    ├── core.ts
    └── route.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;fuma-comment/apps/docs/app/api at main · fuma-nama/fuma-comment&quot;
image: &quot;https://repository-images.githubusercontent.com/717080536/f375c8ab-da9a-4716-973e-92b47f38244a&quot;
description: &quot;user friendly, beautiful comment area to your blog - fuma-nama/fuma-comment&quot;
url: &quot;https://github.com/fuma-nama/fuma-comment/tree/main/apps/docs/app/api&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;경로에있는 파일들을 붙여넣고 경로들만 수정해주면됩니다.&lt;/p&gt;
&lt;h3&gt;Github OAuth 발급&lt;/h3&gt;
&lt;p&gt;이때 NextAuth에서 Github OAuth 토큰을 사용하기 때문에&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;providers: [
	GithubProvider({
		clientId: env.GITHUB_ID, // 하이라이트
		clientSecret: env.GITHUB_SECRET,  // 하이라이트
		profile(profile: GithubProfile) {
			return {
				id: profile.id.toString(),
				name: profile.name || profile.login,
				email: profile.email,
				image: profile.avatar_url,
			};
		},
	}),
],
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OAuth 를 발급받고 설정해주어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;profile -&amp;gt; Settings -&amp;gt; Developer Settings&lt;/code&gt; 를 들어가면 아래의 화면이 나옵니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;여기에서 &lt;code&gt;OAuth Apps&lt;/code&gt;를 클릭하여 이동해줍니다. &lt;code&gt;New OAuth app&lt;/code&gt;을 누르고&lt;/p&gt;
&lt;p&gt;개발 환경이라면 &lt;code&gt;localhost:3000&lt;/code&gt;.
배포 환경이라면 배포 url 을 입력해줍니다. callback URL에는 &lt;code&gt;/api/auth/callback/github&lt;/code&gt;를 추가해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
개발과 배포 환경에서 따로 동작하게 하기 위해서 OAuth를 각각 발급받으시는 것을 추천드립니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그리곤 Secret을 발급 받고 Client ID와 Client Secret의 값을 환경변수 &lt;code&gt;GITHUB_ID&lt;/code&gt;, &lt;code&gt;GITHUB_SECRET&lt;/code&gt;에 binding해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;NextAuth&lt;/h3&gt;
&lt;p&gt;NextAuth관련 환경변수도 설정해주어야 합니다. 아래 링크에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;Options | NextAuth.js&quot;
image: &quot;https://next-auth.js.org/img/logo/logo-xs.png&quot;
description: &quot;Environment Variables&quot;
url: &quot;https://next-auth.js.org/configuration/options#nextauth_secret&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;등으로 설정할 수 있습니다. 특히 &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt;은 공식문서에서 확인할 수 있듯이 프로젝트의 root에서 &lt;code&gt;npx auth secret&lt;/code&gt;을 실행하면 &lt;code&gt;NEXTAUTH_SECRET&lt;/code&gt;의 alias 인 &lt;code&gt;AUTH_SECRET&lt;/code&gt;으로 &lt;code&gt;.env.local&lt;/code&gt;에 setting해줍니다. 이걸 이름만 바꿔서 사용하시면됩니다.&lt;/p&gt;
&lt;h3&gt;Uploadthing&lt;/h3&gt;
&lt;p&gt;uploadthing은 댓글에서 이미지를 사용하기 위해 필요합니다. free tier 에서는 2gb를 기본으로 제공하고 있습니다. 간단한 블로그에서 사용할 만큼은 되는 것 같아서 우선 사용하고 있습니다. 만일 더 필요하게 된다면. 다른 방법을 알아봐야겠죠. S3로 호환이 되니 cloudflare R2를 사용해도 되겠네요.&lt;/p&gt;
&lt;p&gt;아무튼, &lt;a href=&quot;https://uploadthing.com/&quot;&gt;홈페이지&lt;/a&gt;에서 회원가입 및 로그인을 해주신다음 &lt;code&gt;Dashboard -&amp;gt; API Key &lt;/code&gt; 에서 &lt;code&gt;UPLOADTHING_TOKEN&lt;/code&gt;을 그대로 .env에 옮겨주시면됩니다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;이제 Route Handler에서의 설정은 모두 끝났습니다. 코드들은 모두 official repo에서 가져오고. 저희가 해야할 것은 env설정 밖에 없었습니다. 설정한 env는 모두 6개입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DATABASE_URL=
GITHUB_ID=
GITHUB_SECRET=
NEXTAUTH_URL=
NEXTAUTH_SECRET=
UPLOADTHING_TOKEN=
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;NextJS Client&lt;/h2&gt;
&lt;p&gt;그리곤 이제 &lt;code&gt;CommentsWithAuth&lt;/code&gt;라는 이름으로 컴포넌트를 만듭니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use client&quot;;

import { signIn } from &quot;next-auth/react&quot;;

import type { CommentsProps } from &quot;@fuma-comment/react&quot;;

import { Comments } from &quot;@fuma-comment/react&quot;;

import { createUploadThingStorage } from &quot;@fuma-comment/react/uploadthing&quot;;

const storage = createUploadThingStorage();

export function CommentsWithAuth(props: Omit&amp;lt;CommentsProps, &quot;auth&quot;&amp;gt;) {
  return (
    &amp;lt;Comments
      storage={storage}
      mention={{
        enabled: true,
      }}
      auth={{
        type: &quot;api&quot;,
        signIn: () =&amp;gt; void signIn(&quot;github&quot;),
      }}
      {...props}
    /&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;fuma-comment/apps/docs/app/(demo)/page.client.tsx at main · fuma-nama/fuma-comment&quot;
image: &quot;https://repository-images.githubusercontent.com/717080536/f375c8ab-da9a-4716-973e-92b47f38244a&quot;
description: &quot;user friendly, beautiful comment area to your blog - fuma-nama/fuma-comment&quot;
url: &quot;https://github.com/fuma-nama/fuma-comment/blob/main/apps/docs/app/(demo)/page.client.tsx&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리곤 적용하고 싶은 nextjs 페이지(&lt;code&gt;page.tsx&lt;/code&gt;), 레이아웃(&lt;code&gt;layout.tsx&lt;/code&gt;)또는 컴포넌트에 &lt;code&gt;import&lt;/code&gt;하면 끝입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;CommentsWithAuth page={slug} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;page props에는 각 페이지의 id또는 slug를 설정하면 페이지 별로 댓글창을 설정할 수 있습니다. 만약 같은 댓글 영역을 쓰고 싶다면 page의 값을 똑같이 맞춰주면 되겠죠.&lt;/p&gt;
&lt;h2&gt;Outro&lt;/h2&gt;
&lt;p&gt;이렇게 &lt;code&gt;fuma-comment&lt;/code&gt;를 이용해서 댓글기능을 만들어봤습니다. 이렇게 간단하게 만들어 쓰기도 좋은데 커스텀할 수 있는 방안이 굉장히 무궁무진해서 더 유용한 것 같습니다. NextAuth, prisma, postgres DB에 대한 지식이 조금만 있어도 자신이 원하는대로 커스텀할 수 있다는 것이 이 프로젝트의 장점인 것 같습니다.&lt;/p&gt;
&lt;p&gt;멋진 프로젝트를 만들어주신 &lt;a href=&quot;https://github.com/fuma-nama&quot;&gt;fuma-nama&lt;/a&gt;님에게도 감사의 말씀을 드리며, 이만 글을 마치겠습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!seealso]
아마 글을 작성하면서 빠진 부분이나 부족한 부분이 있을 것으로 생각됩니다. 댓글이나 메일을 남겨주시면 최대한 빠른 시일내에 답변드리겠습니다. 감사합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;&lt;em&gt;2025-08-03 추가&lt;/em&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;strong&gt;블로그 이전 및 기능 변경 안내&lt;/strong&gt;
현재 블로그는 &lt;strong&gt;Astro&lt;/strong&gt; 기반으로 이전되었으며, 댓글 시스템 또한 다시 &lt;strong&gt;Giscus&lt;/strong&gt;를 사용하고 있습니다.
fuma-comment는 훌륭한 시스템이지만, 정적 사이트(Static Site)에 더 적합한 Giscus로 되돌아가게 되었습니다. 따라서 이 글에서 소개하는 &lt;code&gt;fuma-comment&lt;/code&gt;는 현재 제 블로그에 적용되어 있지 않으니, &lt;strong&gt;기술 아카이빙 목적&lt;/strong&gt;으로 참고해 주시면 감사하겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><category>fuma-comment</category><category>postgres</category><category>nextatuh</category><category>prisma</category><category>uploadthing</category><category>github-oauth</category><author>Jinmu Go</author></item><item><title>책과 영화</title><link>https://jinmugo.com/posts/book-and-movie/</link><guid isPermaLink="true">https://jinmugo.com/posts/book-and-movie/</guid><description>책과 영화에 대해</description><pubDate>Tue, 16 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;[!quote]&lt;/p&gt;
&lt;p&gt;Q. 요즘은 책 말고 영화나 드라마로 이야기를 표현하는 수단이 미디어 쪽으로 확장되는데, 영화 감상이 책 읽는 것만큼 교양과 지혜 를 쌓는데 도움이 될까요?&lt;/p&gt;
&lt;p&gt;A. 영화는 말하자면 술같은거고요, 책은 물같은거에요. 책은 우리를 좋은 의미에서 차갑게 만들어주고, 영화는 좋은 의미에서 뜨겁 게 만드는데요, 그런데 이성(理性)은 기본적으로 차가운겁니다. 그러니 지금 말씀하신 분의 의견에 따라 답변을 드린다면 &quot;교양에 관한 한, 영화는 책을 영원히 따라가지 못할 것이다.&quot; 왜냐하면 이성(理性)의 속성 자체가 물쪽에 가까워요, 불이 아니라.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;내 원래 전공은 영화/영상 관련이다. 그 때문에, 대학에 다닌 시절에는 영화를 많이 보고 이에 대한 감상과 비평을 자주 작성하였는데, 계속 영화를 보고 글을 읽다 보니 위와 비슷한 궁금증이 일었었다. 내가 감명 깊게 보았던 &lt;a href=&quot;https://www.imdb.com/title/tt1216496/&quot;&gt;&amp;lt;마더&amp;gt;&lt;/a&gt;, &lt;a href=&quot;https://www.imdb.com/title/tt0817225/&quot;&gt;&amp;lt;밀양&amp;gt;&lt;/a&gt;, &lt;a href=&quot;https://www.imdb.com/title/tt1287878/&quot;&gt;&amp;lt;시&amp;gt;&lt;/a&gt;와 같은 영화들은 그동안 내가 읽었던 책들보다도 피부에 와 닿게 교양과 지혜를 심어주었기 때문이다. &quot;피부에 와 닿게&quot;, 비교적 영화는 책에 비해 청자에게 별다른 인지적 노력을 요구하지 않는 것처럼 보인다. 다시 말해, 우리가 영화를 볼 때 우리는 어떤 인물이 어떤 행동을 하는지 상상할 필요가 없이 그저 화면에 나타난 감독/작가의 연출을 보고 듣는다. 감독/작가가 전하고싶은 이야기를 실제 우리가 살아가는 세계의 모습을 빌려 눈앞에 보여주면서 설명하기 때문에 우리는 책을 보는 것보다 &quot;피부에 와닿게&quot; 전달하고 싶은 메시지를 느낄 수 있는 것이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;즉, 우리가 생각해서 깨닫는 것보다 보여줌으로써 깨닫는 것이 영화에서는 더 빈번하게 일어난다고 생각한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그렇기 때문에 이동진 평론가님이 말씀하신, &quot;영화는 우리를 뜨겁게 만든다&quot;라는 문장을 듣고 나서 고개를 끄덕일 수밖에 없었다. 완전히는 아니지만 내가 나름대로 생각한 의미를 말하자면, 영화는 우리에게 무엇을 &quot;보여주고&quot; 있기 때문에 더 직관적이다. 우리가 무엇에 대해 깊이 생각하기도 전에 결과를 보여준다. 그렇기 때문에 영화는 어떤 면에서 수동적이라고 말할 수 있다. 그리고 우리는 영화의 모습들을 보고 희로애락을 &quot;느낀다&quot;. 그렇기 때문에 영화는 우리의 마음에 더 뜨겁게 감각적으로 다가온다. 영화의 시청각적인 정보가 그대로 전달되기 때문에.
오해하지 말길 바란다. 뜨겁다는 건 나쁘다는 뜻이 아니다.&lt;/p&gt;
&lt;p&gt;영화와 일으키는 그 뜨거움은 어쩌면 우리를 살아가게 할 수 있다. 현실의 차가움에서 잠시나마 느낄 수 있는 따스함은 다시 마주한 현실 앞에서 일어설 힘을 주기도 한다. 그래서 인생에서 얼마간의 뜨거움은 필요하다. 이 역할을 영화가 맡아줄 수 있다.
하지만, 이동진 평론가님이 말씀하셨듯이 &quot;&lt;strong&gt;이성(理性)은 기본적으로 차갑&lt;/strong&gt;&quot;다. 만약, 당신이 감명 깊게 본 영화가 있고 그것에 대해 일정 기간 숙고하지 않았다면 당신은 그것을 쉬이 잊어버리기도 혹은 그것이 주는 메시지를 온전히 받아들이지 못했을 수 있다. 왜냐하면 영화가 주는&quot;뜨거움&quot;은 언젠가 사그라들 것이기 때문이다. 영화에서 얻어온 온도가 이성(理性)에게 영향을 주기 위해서는 일종의 &quot;변환의 과정&quot;을 거쳐야 한다. 이 &quot;변환의 과정&quot;은 우리의 내면에서 계속해서 곱씹고 우리의 언어로 바꾸는 과정일 것이다.
이러한 관점에서 보았을 때, 책은 영화와 반대되는 성질을 가진다. 책은 우리에게 &quot;보여주지&quot; 않고 &lt;strong&gt;글을 통해 사유&lt;/strong&gt;(思惟)하게 한다.&lt;/p&gt;
&lt;p&gt;이 과정에서 책은 영화가 취해야 할 &quot;변환의 과정&quot;을 내포하고 있다. 글을 읽어 내려갈수록 책에 대한 세계를 조직적으로 구축하고 그 안에서 이루어지는 정보들을 우리 나름의 언어로 해석한다.
그렇기 때문에 &quot;교양에 관한 한, 영화는 책을 영원히 따라가지 못할 것이다.&quot;라고 말씀하신 이동진 평론가님의 말에 나는 공감할 수 있었다. 영화는 그것의 한계 때문에 우리의 이성(理性)적인 사유(思惟)를 어쩔 수 없이 방해한다. 물론, 어떤 영화들은 생각하지 않고 마음속에서 떠오른다.
그건 이성(理性)이 기능한 게 아니라 다른 무언가가 우리에게 작용한 것이다. 결론적으로 우리는 책과 영화 둘 다 적절히 우리 생을 위해 사용해야 한다. 영화를 연료 삼아 살아가고 책을 양분 삼아 우리 앞의 문제들을 헤쳐 나가야 한다. 대체로 냉정한 현실은 뜨거움만으로는 해결되지 않는다. 치밀하고 전략적으로 조직해야 우리 삶의 문제들을 해결할 수 있기 때문에 적절히 배분하여 삶을 살아가야 할 것이다.&lt;/p&gt;
</content:encoded><category>이동진</category><category>책과 영화</category><author>Jinmu Go</author></item><item><title>CES 2026 참관 계획</title><link>https://jinmugo.com/posts/ces-2026-planning/</link><guid isPermaLink="true">https://jinmugo.com/posts/ces-2026-planning/</guid><description>CES 2026에서 살펴볼 AI·스마트홈·디지털 헬스 기업과 혁신상 제품을 분류하고 현장 참관 동선을 계획한 메모.</description><pubDate>Tue, 09 Dec 2025 11:27:34 GMT</pubDate><content:encoded>&lt;h2&gt;Category&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;AI / Enterprise&lt;/li&gt;
&lt;li&gt;Smart Cities / IoT&lt;/li&gt;
&lt;li&gt;Cloud Computing&lt;/li&gt;
&lt;li&gt;Smart Home &amp;amp; Appliances&lt;/li&gt;
&lt;li&gt;Fashion tech&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;관심 기업 목록&lt;/h2&gt;
&lt;h3&gt;AI&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/noonchee-vx-platform/&quot;&gt;Noonchee-VX Platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Smart Home&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/pila-mesh-home-battery/&quot;&gt;Pila Mesh Home Battery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/touchhl-ai-house-platform/&quot;&gt;touchHL AI house platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/somanest-mattress/&quot;&gt;SomaNest Mattress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/acoustic-eye/&quot;&gt;Acoustic Eye&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/balance-ai-rejuvenation-shower-system/&quot;&gt;BALANCE AI Rejuvenation Shower System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/balance-medi-water-ai/&quot;&gt;BALANCE Medi Water AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/glomensio-firefly-gen-3/&quot;&gt;Glomensio Firefly Gen 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/heymirror-smart-dressing-mirror/&quot;&gt;HEYMIRROR Smart Dressing Mirror&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/mova-mobius-60/&quot;&gt;MOVA MOBIUS 60&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Fashion tech&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/littlebird-safety-tracker/&quot;&gt;Littlebird Safety Tracker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Food tech&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/gardyn-studio-2/&quot;&gt;Gardyn Studio 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Digital health&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/bi-o-biohacking-lens/&quot;&gt;bi-O Biohacking Lens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/pf-sweat-patch-non-invasive-continuous-lactate-sensor/&quot;&gt;PF-Sweat Patch: Non-invasive &amp;amp; Continuous Lactate Sensor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/tsense/&quot;&gt;tSense&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Vehicle tech &amp;amp; advanced mobility&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/ai-transformer-home-trailer-ai-tht/&quot;&gt;AI Transformer Home Trailer (AI-THt)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Home Appliances&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/allcom-smart-tumbler-precise-baby-formula-control/&quot;&gt;Allcom Smart Tumbler – Precise Baby Formula Control&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/nitrogen-coffee-machine/&quot;&gt;Nitrogen Coffee Machine&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Computer hardware &amp;amp; components&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/anker-prime-charger-160w-3-ports-smart-display/&quot;&gt;Anker Prime Charger (160W, 3 Ports, Smart Display)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Computer peripherals &amp;amp; accessories&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/wizpr-ring/&quot;&gt;Wizpr Ring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Edtech&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/chalk-mastering-concept-faster-through-visual-llm/&quot;&gt;CHALK: Mastering Concept Faster through Visual LLM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/mentorlens-ai-tutor-glass-supporting-personalized-studies-for-everyone/&quot;&gt;MentorLens – AI Tutor Glass Supporting Personalized Studies for Everyone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;headphones &amp;amp; personal audio&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;harman&lt;/li&gt;
&lt;li&gt;shokz&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/perisphere-audio-meets-vision/&quot;&gt;Perisphere: Audio Meets Vision&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;sports &amp;amp; fitness&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/orphe-insole/&quot;&gt;ORPHE INSOLE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;mobile devices, acccessories &amp;amp; apps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/otterbox-symmetry-series-cactus-leather-embroidery/&quot;&gt;OtterBox Symmetry Series Cactus Leather Embroidery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ces.tech/ces-innovation-awards/2026/seeru/&quot;&gt;SEERU&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;참관 계획&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;일자&lt;/th&gt;
&lt;th&gt;코스&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1월 6일오전&lt;/td&gt;
&lt;td&gt;[LVCC North C5 Entrance] &amp;gt; Samsung SDS(N110) &amp;gt; N1 Entrance &amp;gt; Deepmind Platform (8353) &amp;gt; Hitachi (8529) &amp;gt; Primech AI (8935) &amp;gt; Segway-Ninebot (9615) &amp;gt; Abaka AI (9647) &amp;gt; PARADISE BRIDGE &amp;gt; [LVCC West Level 3] &amp;gt; Mobileye (W326) &amp;gt; Hyundai Mobis (W319) &amp;gt; [LVCC West Level 2]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1월 6일오후&lt;/td&gt;
&lt;td&gt;[Venetian Level 2 Hall A] &amp;gt; Innovation Awards Showcase (50043) &amp;gt; swisstech Pavilion (51239) &amp;gt; TP-Link Systems Inc. (51923) &amp;gt; WYBOTIC Co.,Ltd (51455) &amp;gt; SmartWings Home LLC (51762) &amp;gt; SwitchBot. Inc (52655) &amp;gt; ORPHE, Inc (54199) &amp;gt; Chamelo (56142)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1월 7일오전&lt;/td&gt;
&lt;td&gt;[Venetian Level 2 Hall A] &amp;gt; Innovation Awards Showcase (50043) &amp;gt; swisstech Pavilion (51239) &amp;gt; TP-Link Systems Inc. (51923) &amp;gt; WYBOTIC Co.,Ltd (51455) &amp;gt; SmartWings Home LLC (51762) &amp;gt; SwitchBot. Inc (52655) &amp;gt; ORPHE, Inc (54199) &amp;gt; Chamelo (56142)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1월 7일오후&lt;/td&gt;
&lt;td&gt;[Venetian Level 1 Hall G] &amp;gt; Canon U.S.A, Inc,&apos;s Business Innovation Group (60509) &amp;gt; Hyundai Motor and Kia ZER01NE-CRADLE (61311) &amp;gt; Samsung Electronics Co., Ltd. (C-Lab) (61511) &amp;gt; Industrial Bank of Korea (62949) &amp;gt; XbotPark (62919) &amp;gt; HongKong Tech (63200) &amp;gt; Korea Institute of Startup &amp;amp; Entrepreneurship Dev (62817) &amp;gt; Postech (61453) &amp;gt; Remarkable (61053) &amp;gt; LG NOVA Zenith Electronics LLC (60301)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1월 8일오전&lt;/td&gt;
&lt;td&gt;[LVCC Central Hall C1 Entrance] &amp;gt; LG Electronics (15004) &amp;gt; Shokz (15823) &amp;gt; Bosch (16203) &amp;gt; Sony Honda Mobility Inc. (20800) &amp;gt; Tuya Global Inc. (16838) &amp;gt; South Hall Connector &amp;gt; [LVCC South Hall 1 &amp;amp; 2] (시간 여유가 있을 경우 자율관람)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</content:encoded><category>CES</category><author>Jinmu Go</author></item><item><title>DHCP with wi-fi</title><link>https://jinmugo.com/posts/dhcp-with-wifi/</link><guid isPermaLink="true">https://jinmugo.com/posts/dhcp-with-wifi/</guid><description>기기가 Wi-Fi 인증과 연결을 마친 뒤 DHCP DORA 과정으로 IP 주소와 네트워크 설정을 받기까지의 흐름을 설명합니다.</description><pubDate>Sun, 21 Sep 2025 10:07:21 GMT</pubDate><content:encoded>&lt;p&gt;이제는 우리 삶에서 뗄래야 뗄 수 없는 Wifi. 최근 네트워크 강의 시간에 DHCP관련 수업을 듣는 중 문득 wifi를 하루도 뺴놓지 않고 쓰고 있는데, 동작원리나 연결이 어떻게 되는지 궁금해졌다. 이번 글에서는 주로 Wi-Fi, DHCP에 대해 정리해보려 한다.&lt;/p&gt;
&lt;h2&gt;Wi-Fi&lt;/h2&gt;
&lt;p&gt;간단히 말하면 공유기(Access Point)가 유선 인터넷 신호를 무선 신호로 변환하여 주변에 뿌려주면, 스마트폰이나 노트북 같은 기기들이 이 신호를 받아 인터넷에 접속하는 원리다. 유선랜(LAN)과 달리 물리적인 케이블 없이 공기 중의 전파를 통해 데이터를 주고 받는다.&lt;/p&gt;
&lt;p&gt;즉, 사용자가 인터넷에 접속하여 웹서핑을 하거나 영상을 보는 등 서비스를 이용한다는 점에서는 일반 유선 인터넷 연결과 본질적으로 동일하다. 와이파이는 그저 인터넷에 연결되는 마지막 구간을 &lt;strong&gt;물리적인 선 대신 무선 전파로 바꾼 것뿐&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;하지만, &quot;무선&quot;이라는 차이점이 기술적으로나 실제 사용 환경에서 몇 가지 중요한 차이를 만들어낸다.
예를 들어 안정성과 속도가 있겠다. 유선 연결은 물리적인 케이블을 통해 데이터가 매우 안정적이고 속도가 빠르다. 반면, 와이파이는 전파를 사용하기 때문에 주변 환경에 영향을 많이 받는다. 하지만 이동성(Mobility)가 굉장히 좋다. 사실 이게 와이파이의 가장 큰 존재 이유이자 장점이지 않을까 싶다. 유선 인터넷은 정해진 장소에서만 사용할 수 있지만,와이파이는 신호가 닿는 범위 내에서는 자유롭게 이동하며 인터넷을 사용할 수 있는 편리함이 있다.&lt;/p&gt;
&lt;p&gt;핫스팟 같은 P2P 방식으로 동작하는 경우를 제외하고 와이파이는 주로 클라이언트-서버 모델로 작동 한다. 일반적으로 우리가 사용하는 와이파이 환경을 기준으로 역할은 아래와 같이 나뉜다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버: 공유기가 서버역할을 한다. 각 기기들의 네트워크 접속을 허용하고 관리하며 데이터를 중계한다.&lt;/li&gt;
&lt;li&gt;클라이언트: 스마트폰, 노트북, 스마트 TV등 공유기에 연결하여 인터넷 서비스를 요청하고 제공받는 모든 기기들이 클라이언트이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;기존 클라이언트-서버 모델과 비슷하게 클라이언트가 서버에게 인터넷 연결을 요청하면, 서버는 요청을 받아 인터넷에 연결시켜주는 구조이다. 그리고 이때 서버가 클라이언트의 요청을 받아 실제 인터넷을 사용할 수 있도록 네트워크 주소(IP)를 할당하는 과정에서 &lt;strong&gt;DHCP가&lt;/strong&gt; 사용된다.&lt;/p&gt;
&lt;h3&gt;Wi-Fi와 DHCP의 관계&lt;/h3&gt;
&lt;p&gt;DHCP가 IP주소를 할당하는 과정에서 사용된다고 했다. 우리가 카페에서 와이파이에 접속할 때 가장 먼저 하는 일은 비밀번호를 입력하는 건데 이 과정은 DHCP와 무슨 관련이 있는걸까.&lt;/p&gt;
&lt;p&gt;결론부터 말하면, &lt;strong&gt;비밀번호를 통한 인증 과정은 DHCP보다 먼저 일어난다&lt;/strong&gt;. 클라이언트가 IP 주소를 DHCP서버에 요청하기 전에, 해당 네트워크에 접속할 자격이 있는지 부터 확인하는 것이다. 전체 흐름을 보면 아래와 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;스캔 (Scan): 스마트폰이 주변의 사용 가능한 Wi-Fi 신호를 탐색한다. 우리가 Wi-Fi 설정 화면에서 보는 익숙한 목록이다.&lt;/li&gt;
&lt;li&gt;인증(Authentication): 사용자가 특정 Wi-Fi(SSID)를 선택하고 비밀번호를 입력한다. 공유기는 이 비밀번호가 맞는지 확인하여 네트워크에 접속을 허가받은 사람인지를 검증한다. 이 단계가 우리가 흔히 &lt;strong&gt;와이파이에 연결한다&lt;/strong&gt;고 생각하는 과정이다.&lt;/li&gt;
&lt;li&gt;연결 (Association): 인증이 성공하면, 공유기는 해당 기기를 자신의 관리 목록에 추가한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;연결단계까지 성공적으로 끝나야, 비로소 스마트폰은 IP 주소를 할당받기 위한 DHCP Discover메시지를 네트워크에 보낼 준비를 마친다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!note] SSID
서비스 세트 식별자(Service Set Identifier)의 약자로, 무선 네트워크(와이파이 네트워크)의 이름을 의미한다. 라우터나 액세스 포인트(AP)가 자신이 제공하는 네트워크를 구분하기 위해 할당하는 고유한 이름이다. 예를 들어, 핸드폰이나 노트북에서 주변 와이파이 목록에 나타는 네트워크 이름이 SSID이다. SSID는 네트워크들을 구분하는 식별자 역할을 하며, 같은 지역 안에서 여러 네트워크가 있을 때 혼동을 방지해 준다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;DHCP&lt;/h2&gt;
&lt;p&gt;Dynamic Host Configuration Protocol 의 줄임말이다. 네트워크에 접속하는 기기들에게 IP 주소를 비롯한 통신에 필요한 정보들을 자동(Dynamic)으로 할당하고 관리해주는 프로토콜이다.&lt;/p&gt;
&lt;p&gt;카페에서 Wi-Fi를 사용하는 상황을 생각해보자. 매일 수십, 수백 명의 손님들이 다양한 기기로 Wi-Fi에 접속한다. 만약 DHCP가 없다면 어떻게 될까? 잠깐만 생각해도 아래의 문제가 발생할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;수동 IP 설정의 문제점&lt;/strong&gt;: 각 손님이 직접 IP 주소를 입력해야 한다면? 어떤 IP가 비어있는지 알 수 없고, 중복된 IP를 설정하면 충돌이 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;관리의 복잡성&lt;/strong&gt;: 카페 주인이 모든 기기의 IP를 일일이 관리하고 기록해야 한다면 현실적으로 불가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DHCP는 이런 문제들을 해결해준다. 기기가 네트워크에 접속하면 자동으로 사용 가능한 IP 주소를 찾아 할당하고, 사용이 끝나면 다시 회수하여 다른 기기에 재할당할 수 있다.&lt;/p&gt;
&lt;h3&gt;DHCP의 동작 과정 (DORA)&lt;/h3&gt;
&lt;p&gt;Wi-Fi에 연결될 때 실제로 일어나는 DHCP의 4단계 과정을 살펴보자. 이 과정을 DORA라고도 부르는데 각 과정의 앞글자를 따서 기억하기 쉽게 만든 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;실제 데이터 전송 내용&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. D - Discover (발견)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 &lt;strong&gt;네트워크에 연결&lt;/strong&gt; 되면 &quot;DHCP 서버가 있나요?&quot;라는 브로드캐스트 메시지를 전송&lt;/li&gt;
&lt;li&gt;이때 클라이언트는 아직 IP 주소가 없으므로 브로드캐스트(255.255.255.255)로 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. O - Offer (제안)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DHCP 서버(223.1.2.5)가 &quot;저는 DHCP 서버입니다! 여기 사용할 수 있는 IP 주소가 있어요&quot;라고 응답&lt;/li&gt;
&lt;li&gt;서버는 사용 가능한 &lt;strong&gt;IP 주소를 포함한 네트워크 설정 정보를 제안&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. R - Request (요청)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 &quot;좋습니다. **이 IP 주소를 사용하고 싶습니다!&quot;**라고 브로드캐스트로 응답&lt;/li&gt;
&lt;li&gt;여러 DHCP 서버가 있을 경우, 특정 서버의 제안을 선택했음을 알림&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. A - ACK (승인)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DHCP 서버가 &quot;좋습니다. 이제 그 IP 주소를 사용하세요!&quot;라고 최종 확인&lt;/li&gt;
&lt;li&gt;이 시점에서 클라이언트는 할당받은 IP 주소와 네트워크 설정을 사용할 수 있게 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이것이 일반적인 DHCP의 연결 과정이다. 하지만 몇가지 궁금한 점이 있다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DHCP는 IP를 노드에 할당하기(인터넷에 연결되기 위해) 사용된다. 그런데 어떻게 클라이언트가 네트워크에 연결되어 브로드캐스트 메시지를 전송할 수 있을까?&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;Discover를 하고 2. Offer를 한다음에 서버가 3. ACK를 하면 (마치 TCP의 3-way handshake 처럼) 연결이 될 수도 있을텐데 왜 Request과정이 필요한 것일까?&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;IP 주소의 유효기간은 없을까? 만약 카페를 나갔다가 내일 다시 오면 어떻게 되는 걸까.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이와 같은 궁금증이 생겨서 찾아보았다.&lt;/p&gt;
&lt;h3&gt;1. IP도 없는데 어떻게 네트워크에 연결해서 브로드캐스트를 하는가.&lt;/h3&gt;
&lt;p&gt;내가 생각한 네트워크 연결은 IP주소를 받아 인터넷 통신이 가능한 OSI 7계층 중 3계층인 Network Layer 이상의 연결을 의미하는 것이었다.
하지만 DHCP Discover 메시지는 그 이전 단계인 2계층 Data Link Layer 연결만으로도 가능하다.&lt;/p&gt;
&lt;p&gt;DHCP의 핵심은 IP 주소가 없는 클라이언트가 네트워크상의 누군지 모르는 DHCP 서버를 찾는 것이다. 이를 위해 자신이 알고 있는 네트워크 전체에 메시지를 뿌리는 &lt;strong&gt;브로드캐스트&lt;/strong&gt;가 필수적이다. TCP는 1:1 연결을 지향하고 브로드캐스트 기능이 없어 DHCP에 사용하기에 부적합하다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;애초에 자신의 IP를 알지 못하는 노드에서 TCP를 쓸 수 조차 없다.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
요약하자면, DHCP의 브로드캐스트는 IP주소가 필요한 3계층(네트워크 계층)이 아닌, 그 이전 단계인 2계층의 통신으로 이루어지기 때문에 가능하다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. Discover -&amp;gt; Offer -&amp;gt; ACK의 순서에 Request가 필요한 이유&lt;/h3&gt;
&lt;p&gt;DHCP에서 Request 과정이 반드시 필요한 가장 큰 이유는 네트워크 상 DHCP 서버가 &lt;strong&gt;하나 이상 존재할 수 있기 때문&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;위에서 알 수 있듯이 클라이언트는 소속된 네트워크 상에서 &lt;strong&gt;브로드캐스트&lt;/strong&gt;메시지를 전송한다. 이렇게 되면 네트워크 내의 모든 DHCP 서버가 이 메시지를 받는다.
만약 서버가 한 대라면 Dicover -&amp;gt; Offer -&amp;gt; ACK 순서로도 가능하겠지만(TCP가 그러한 것처럼)
만약 서버가 두 대 이상이라면, 클라이언트는 다른 IP주소를 담은 여러개의 Offer 메시지를 받게 된다. 클라이언트는 이런 제안들 중 하나를 선택해야 하고 이때 Request 메시지를 다시 &lt;strong&gt;브로드 캐스트&lt;/strong&gt;로 보내 예를 들어 &quot;나는 A 서버가 제안한 192.168.0 IP주소를 사용할거야&quot;같이 네트워크 전체에 알린다.
이렇게 브로드 캐스트로 알리는 이유는 선택받지 못한 다른 서버들을 위함이다. 선택받지 못한 서버들은 해당 클라이언트가 내 제안(Offer)를 선택하지 않았음을 인지하고, 제안했던 IP주소를 다른 클라이언트에게 할당할 수 있도록 &lt;strong&gt;회수&lt;/strong&gt;한다. 이렇게 되면 다른 클라이언트들을 위한 자리를 확보할 수 있기 때문에 더욱 효율적이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
만약 Request과정이 없다면, 클라이언트는 어떤 서버의 제안을 수락했는지 다른 서버들에게 알릴 방법이 없을 뿐더러 어떤 DHCP서버를 선택할지 요청을 전달할 수도 없다. Request 단계는 여러 DHCP 서버 중 하나를 선택하고, 선택되지 않은 서버들에게 그 사실을 알려서 IP 주소를 효율적으로 관리하기 위해 필수적인 과정이라 할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3. IP 주소의 유효기간&lt;/h3&gt;
&lt;p&gt;IP주소는 한정된 자원이기 때문에, 네트워크를 떠난 기기가 IP를 계속 차지하고 있으면 자원이 고갈된다. 카페처럼 유동 인구가 많은 곳에서는 기기들이 계속 바뀌므로, 사용하지 않는 IP를 회수하여 새로운 기기에 재할당해야 한다.&lt;/p&gt;
&lt;p&gt;DHCP 서버는 IP 주소를 할당할 때 &quot;이 IP는 N시간 동안 유효 합니다&quot;라는 임대 기간을 알려준다.  클라이언트는 이 기간동안만 해당 IP를 사용할 수 있으며, 기간이 만료되기 전에 서버에 갱신 요청을 해야 한다.&lt;/p&gt;
&lt;h3&gt;DHCP서버에 IP를 할당 받고 나갔다가 다시 오면?&lt;/h3&gt;
&lt;p&gt;이 경우, IP 임대 기간이 만료되었는지 여부에 따라 동작이 달라진다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;임대 기간이 만료되지 않았을 경우 (예: 잠시 나갔다 온 경우)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트(내 스마트폰)는 이전에 할당받았던 IP 주소를 기억하고 있다.&lt;/li&gt;
&lt;li&gt;네트워크에 다시 접속하면, 전체 &lt;strong&gt;DORA 과정을 거치지 않고&lt;/strong&gt; 바로 해당 DHCP 서버에게 &quot;저번에 쓰던 IP 주소 계속 써도 될까요?&quot;라고 직접 &lt;strong&gt;&lt;code&gt;Request&lt;/code&gt;&lt;/strong&gt; 메시지를 보낸다.&lt;/li&gt;
&lt;li&gt;서버가 허락하면 **&lt;code&gt;ACK&lt;/code&gt;**를 받아 더 빠르게 IP를 할당받고 연결됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;임대 기간이 만료되었을 경우 (예: 다음 날 다시 온 경우)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;내 스마트폰이 가지고 있던 IP 주소의 유효기간이 지났으므로, 그 IP는 DHCP 서버에 의해 회수되어 다른 사람이 사용하고 있을 수 있다.&lt;/li&gt;
&lt;li&gt;내 스마트폰은 유효한 IP가 없는 상태이므로, 처음 접속할 때와 똑같이 &lt;strong&gt;&lt;code&gt;Discover&lt;/code&gt; 부터 시작하는 전체 DORA 4단계 과정&lt;/strong&gt;을 다시 거친다.&lt;/li&gt;
&lt;li&gt;이때 이전에 사용했던 IP를 다시 받을 수도 있지만, 대부분의 경우 사용 가능한 다른 IP 주소를 새로 할당받게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;마무리&lt;/h2&gt;
&lt;p&gt;지금까지 Wi-Fi, DHCP에 대해 알아보았다. 다시한번 정리해보자.
만약 카페에서 Wi-Fi에 연결하여 인스타그램을 보려고 한다 가정했을때,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;탐색 (Scan):&lt;/strong&gt; 스마트폰이 &quot;Cafe_WiFi&quot; 신호를 발견.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인증 (Authentication):&lt;/strong&gt; &quot;Cafe_WiFi&quot;를 누르고 비밀번호를 입력. 카페 공유기는 비밀번호가 맞는지 확인.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연결 (Association):&lt;/strong&gt; 공유기는 스마트폰을 관리 목록에 등록한다. 이제 공유기와 데이터를 주고받을 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DHCP 시작 (DORA):&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;D(Discover):&lt;/strong&gt; 스마트폰이 네트워크 전체에 &lt;strong&gt;브로드캐스트&lt;/strong&gt; Discover 요청을 보낸다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;O(Offer):&lt;/strong&gt; 공유기(DHCP 서버)들이 그 메시지를 듣고 응답합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;R(Request):&lt;/strong&gt; 스마트폰은 그 제안을 수락하며, &lt;strong&gt;다른 서버들도 알 수 있도록&lt;/strong&gt; 다시 한번 브로드 캐스트로 알린다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A(ACK):&lt;/strong&gt; 공유기가 최종적으로 승인하며 IP 할당을 확정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인터넷 통신 시작:&lt;/strong&gt; 이제 내 스마트폰은 고유 주소를 가지고, 공유기를 통해 인스타그램 서버와 데이터를 주고받을 수 있게된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;무심코 사용하던 와이파이 연결의 이면에는 이처럼 여러 단계의 약속들이 아주 짧은 시간 안에 유기적으로 동작하고 있었다. 이 글을 통해 와이파이와 DHCP의 동작 원리를 이해하는 데 도움이 되었기를 바랍니다.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;개인적으로 공부하고 정리한 글이라 부정확하고 부족한 부분이 있을 수 있습니다. 댓글 남겨주세요!&lt;/em&gt;&lt;/p&gt;
</content:encoded><category>DHCP</category><category>Wi-fi</category><author>Jinmu Go</author></item><item><title>DNS (Domain Name System) DeepDive - 배경과 시스템</title><link>https://jinmugo.com/posts/dns-deepdive-1/</link><guid isPermaLink="true">https://jinmugo.com/posts/dns-deepdive-1/</guid><description>HOSTS.TXT의 중앙 관리 한계에서 계층화·위임 기반의 DNS가 등장하기까지, 도메인 이름과 분산 관리 구조의 배경을 살펴봅니다.</description><pubDate>Sun, 08 Jun 2025 12:58:28 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;The Internet is for Everyone&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;문득 인터넷을 돌아다니다가 &lt;strong&gt;DNS(Domain Name System)&lt;/strong&gt; 의 동작에 대해 궁금해졌습니다. 왜 DNS를 만든지도 궁금할 뿐더러 현재 퍼져있는 여러 도메인들(.com, .net, .kr)을 어떻게 관리하는지도 궁금했습니다. 그러던 중 마침 &lt;a href=&quot;https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=282893007&amp;amp;partner=openAPI&amp;amp;start=api&quot;&gt;DNS 실전 교과서&lt;/a&gt;라는 책을 발견하여 이 책을 기반으로 시리즈로 블로그에 정리해보려 합니다.&lt;/p&gt;
&lt;h2&gt;IP주소와 이름&lt;/h2&gt;
&lt;p&gt;전 세계에는 다른 컴퓨터에 정보나 서비스를 제공하는 &apos;서버&apos;라고 불리는 컴퓨터와 스마트폰, 라우터 등 수 많은 컴퓨터가 연결되어 있습니다. 이렇게 네트워크에 연결된 컴퓨터를 가리켜 &lt;strong&gt;호스트(hosts)&lt;/strong&gt; 라고 부릅니다.
&lt;/p&gt;
&lt;p&gt;이렇게 많은 컴퓨터가 연결되어 있다 보니 호스트끼리 통신할 때는 통신할 상대를 어떻게든 지정해야 합니다. 또 반대로 상대 호스트 입장에서 봤을 때 누가 통신을 해왔는지 알 수 없다면 해당 통신을 허용해도 되는지 안 되는지를 판별하기 어렵습니다.&lt;/p&gt;
&lt;p&gt;인터넷에서는 이때 &lt;strong&gt;IP(Internet Protocol)주소&lt;/strong&gt;를 사용합니다. IP주소는 통신할 상대를 식별할 때 사용되는 주소입니다.&lt;/p&gt;
&lt;p&gt;하지만 IP주소는 숫자의 나열로 되어 있어 외우기 어렵고 틀리기 쉽습니다. 또 IP주소는 관리하는 쪽의 사정으로 때때로 변하기도 합니다. 만약 변경된 것을 모르고 이전 IP 주소를 지정하게 되면 완전히 다른 상대와 연결될 수도 있습니다.&lt;/p&gt;
&lt;p&gt;이런 불편을 해소하기 위해 외우기 쉽고 사용하기 쉬운 &lt;strong&gt;이름(name)&lt;/strong&gt; 으로 통신할 상대를 지정하는 방법이 고안되었습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!note] 어드레싱과 네이밍
정해진 규칙에 따른 주소로, 통신할 상대인 송신자나 수신자를 지정하는 것을 **어드레싱(addressing)**이라고 하고 통신할 상대나 서비스에 이름을 붙이고 주소와 매핑하는 것을 **네이밍(naming)**이라고 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;IP주소와 이름의 매핑을 관리하자&lt;/h2&gt;
&lt;p&gt;앞 장에서 설명한 것과 같이 인터넷에서는 &lt;strong&gt;IP주소로 상대를 식별&lt;/strong&gt;하지만, 실제로 &lt;strong&gt;통신할 상대를 지정할 때&lt;/strong&gt;는 이름을 사용하면 편합니다. 그래서 이름과 IP주소의 대응을 하나의 표로 정리해 두면 상대를 지정할 때 이름에 대응하는 IP주소를 찾기만 하면 되어 IP주소를 일일이 외우지 않아도 됩니다.&lt;/p&gt;
&lt;p&gt;인터넷 초기 시절에는 이를 위한 표로 &lt;code&gt;HOSTS.TXT&lt;/code&gt;라는 텍스트파일에 인터넷에 연결된 모든 호스트의 IP주소와 이름의 대응을 기재하여 관리했습니다.&lt;/p&gt;
&lt;p&gt;IP 주소는 인터넷 전체에서 고유한 번호여야 하므로 어느 기기가 어느 IP주소를 사용 중인지 통일성 있게 관리해야 합니다. 그렇기에 &lt;code&gt;HOSTS&lt;/code&gt;파일은 이름이나 번호가 중복되지 않도록 SRI-NIC(Stanford Research Institue Network Information Center)에서 일원화 관리를 했습니다. 이처럼 인터넷에서 공통으로 사용되는 이름이나 번호를 일원화 관리하는 조직을 **레지스트리(registry)**라고 합니다.&lt;/p&gt;
&lt;p&gt;당시 인터넷에 접속하고 싶은 조직은 SRI-NIC에 접속 신청을 했습니다. 그러면 SRI-NIC이 각 조직의 신청 내용을 확인하고 심사해 주소를 할당하고 그 결과를 &lt;code&gt;HOSTS&lt;/code&gt;파일에 등록했습니다. 그리고 &lt;code&gt;HOSTS&lt;/code&gt;파일을 인터넷에 공개하고 이용자는 그 파일을 다운로드해서 사용하는 방식이 기본이었습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;집중 관리에서 분산 관리로.&lt;/h2&gt;
&lt;p&gt;인터넷이 발전함에 따라 접속하려는 호스트의 수가 늘어나면서 SRI-NIC에 신청하는 양이 많아졌고, 점차 처리 속도가 늦어졌습니다. 인터넷에 접속하고 싶은 조직이 이대로 증가하게 되면 관리 구조에 한계가 올 것은 자명했습니다.&lt;/p&gt;
&lt;p&gt;이런 고민에서 나온 것이 바로 &lt;strong&gt;계층화&lt;/strong&gt;와 &lt;strong&gt;위임&lt;/strong&gt;을 통한 분산 관리 구조입니다.&lt;/p&gt;
&lt;h3&gt;계층화와 위임&lt;/h3&gt;
&lt;p&gt;계층화와 위임이라는 사고방식은 회사처럼 규모가 있는 조직을 관리할 때 사용합니다.
회사에서는 업무 종류에 따라 부서를 만들고, 각 부서의 역할을 정해 조직 체계를 명확히 합니다. 이것이 &apos;계층화&apos;입니다. 그리고 각 부서의 부서장을 정하고 부서장에게 관리를 맡깁니다. 이것이 &apos;위임&apos;입니다.
현대 인터넷 체계는 이러한 계층화와 위임의 구조를 이용해 호스트의 IP주소와 이름을 관리합니다.&lt;/p&gt;
&lt;p&gt;인터넷에서는 &lt;strong&gt;네임 스페이스(name space)&lt;/strong&gt; 라고 불리는 하나의 공간을 전체에서 공유하고 있습니다. 그래서 네임 스페이스의 일부를 분할하고, 잘라낸 네임 스페이스를 신뢰할 수 있는 다른 사람에게 위임하는 구조가 채택되었습니다. &lt;strong&gt;위임한 쪽에서는 그 네임 스페이스를 누구에게 위임했는지에 대한 정보만 관리&lt;/strong&gt;하고, 해당 네임 스페이스의 관리 책임은 위임받은 쪽이 갖게 됩니다.&lt;/p&gt;
&lt;p&gt;계층화와 위임을 도입하면 다음과 같은 두 가지 장점이 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;관리를 분산하기 때문에 각 관리자의 부담을 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;조직의 성장 및 변화에 유연하게 대응할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;즉, 관리 범위가 커져서 관리가 어렵게 되면 그 범위를 필요에 따라 분할하고 새로운 관리자에게 위임하기만 하면 됩니다.
그런데 이렇게 계층화와 위임을 해서 각각의 관리자에게 &lt;code&gt;HOSTS&lt;/code&gt;파일을 갖게 한다면 중복문제는 어떻게 할까요? 전술했듯이 초창기 인터넷에선 SRI-NIC가 이름이나 번호가 중복되지 않도록 일원화 관리를 했습니다.&lt;/p&gt;
&lt;p&gt;분산 관리 체계로 바뀌더라도 이 장점을 잃지 않도록 &lt;strong&gt;하나의 정점을 두는 트리구조&lt;/strong&gt;로 설계되었습니다. 트리 구조에서는 가지가 뻗어 나간 쪽의 각 계층에서 이름이 중복되지 않도록 관리하여 네임 스페이스 고유한 이름을 갖도록 보장할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!caution] 계층화와 위임에서 주의할 점
계층화와 위임을 도입해서 얻는 장점은 크지만, 이러한 분산관리를 실현하기 위해서는 각 계층을 담당하는 기관이 자신이 담당하는 부분을 잘 관리하고 책임을 다한다는 전제가 필요합니다.
예를들어, &lt;a href=&quot;https://www.paloaltonetworks.co.kr/cyberpedia/what-is-an-nxnsattack&quot;&gt;NXNSAttack&lt;/a&gt; 과 같은 도메인 이름에 악의를 가지고 의도적으로 부적절한 설정을 하여 DNS를 공격할 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;도메인 이름의 구성&lt;/h2&gt;
&lt;p&gt;인터넷에서는 분할된 각 네임 스페이스의 범위를 &lt;strong&gt;도메인(domain)&lt;/strong&gt;, 그 범위를 식별하기 위해 붙여진 이름을 **도메인 이름(domain name)**이라고 부릅니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;도메인 이름이란?&lt;/h3&gt;
&lt;p&gt;각 계층의 이름을 중복되지 않게 트리 형태로 관리하면 도메인 이름은 인터넷상에서 고유해집니다. 그리고 이런 도메인 이름과 IP 주소를 적절히 매핑하면 도메인 이름을 인터넷 호스트를 특정하기 위한 &lt;strong&gt;식별자&lt;/strong&gt;로 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;예를들어, &lt;code&gt;https://jinmu.me&lt;/code&gt;에서 &lt;code&gt;jinmu.me&lt;/code&gt;부분이 &apos;도메인 이름&apos;입니다.&lt;/p&gt;
&lt;h3&gt;도메인 이름의 구성&lt;/h3&gt;
&lt;p&gt;도메인 이름은 문자열을 &apos;.(dot)&apos;으로 연결한 형태로 구성됩니다. 각각의 문자열을 &lt;strong&gt;라벨(label)&lt;/strong&gt; 이라고 합니다. 사실, 도메인 이름의 마지막에도 점(.)이 붙어있는데 보통은 생략합니다. 도메인 이름의 마지막에 붙여진 점은 계층 구조의 정점인 &lt;strong&gt;루트(root)&lt;/strong&gt; 를 나타내며, 루트를 기준으로 오른쪽에서 순서대로 다음과 같이 부릅니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TLD(Top Level Domain)&lt;/li&gt;
&lt;li&gt;2LD(2nd Level Domain)&lt;/li&gt;
&lt;li&gt;3LD(3rd Level Domain)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;서브 도메인(subdomain)&lt;/h3&gt;
&lt;p&gt;어떤 네임 스페이스의 범위가 다른 네임 스페이스의 범위에 속할 때 &lt;strong&gt;서브 도메인&lt;/strong&gt;이라고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;서브 도메인은 해당 도메인의 관리자가 자유롭게 만들 수 있습니다. 그리고 만든 서브 도메인을 다른 사람에게 위임할지에 대한 여부도 정할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;도메인 이름을 사용하기 위해 생긴 DNS&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;DNS&lt;/strong&gt;는 &lt;code&gt;Domain Name System&lt;/code&gt;의 줄임말입니다. 도메인 이름의 도입을 전제로 하여 개발된 시스템이기에 DNS도 계층화와 위임에 의한 분산 관리 구조를 채택했습니다.&lt;/p&gt;
&lt;p&gt;DNS는 도메인 이름과 IP주소의 대응을 관리하고 이용자의 요청에 따라 도메인 이름에 대응하는 IP주소를 찾습니다. 이것을 &lt;strong&gt;이름 풀이(Name Resolve)&lt;/strong&gt; 라고 합니다.&lt;/p&gt;
&lt;p&gt;DNS의 기본 목표는 각 계층의 관리자로부터 필요한 정보를 얻어 도메인 이름의 계층 구조를 따라가, 최종 목적인 IP 주소를 얻는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;DNS 계층화와 위임의 구조&lt;/h3&gt;
&lt;p&gt;DNS에서는 도메인 이름에 대응하는 형태로 관리 범위를 계층화하고, 위임해서 관리를 분산합니다. 위임에 의해 관리하게 된 범위를 존(zone)이라고 합니다. DNS에서 위임한 사람(위임자)과 위임받은 사람(위임처)은 부모와 자식 관계가 됩니다.&lt;/p&gt;
&lt;p&gt;존을 위임하기 위해서는 우선 서브 도메인을 만드는데, 위임자가 만듭니다. 예를 들어, me에 서브 도메인을 만들고 다른 사람에게 위임합니다. 그러면 &lt;code&gt;me&lt;/code&gt;이 부모가 되고 &lt;code&gt;me&lt;/code&gt;로부터 위임받은 위임처는 자식이 되는 &lt;strong&gt;부모-자식 관계&lt;/strong&gt;가 성립합니다. 그리고 각 존의 관리자는 &lt;strong&gt;네임 서버&lt;/strong&gt;라는 서버로 정보를 관리합니다.&lt;/p&gt;
&lt;p&gt;네임 서버가 관리하는 정보는 아래와 같이 두 종류입니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;존에 존재하는 호스트의 도메인 이름과 IP주소
&lt;ul&gt;
&lt;li&gt;각 존의 네임 서버가 도메인 이름과 IP주소의 매핑을 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위임 정보
&lt;ul&gt;
&lt;li&gt;위임처(자식)의 네임 서버 정보로, 위임자(부모)의 네임 서버가 관리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;DNS와 레지스트리의 관계&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;#ipjusowa-ireumyi-maepingeul-gwanrihaja&quot;&gt;IP주소와 이름의 매핑을 관리하자&lt;/a&gt;에서 설명했듯이 &lt;code&gt;HOSTS&lt;/code&gt; 파일에 의한 관리는 하나의 레지스트리(SRI-NIC)가 이름이나 번호를 일원화 관리하는 간결한 형태를 가집니다. 도메인 이름과 DNS를 도입하는 것으로 관리자의 부담을 줄이고 보다 유연한 관리를 실현할 수 있지만 이는 이름을 관리하는 레지스트리가 여러 개 생겨난 것을 의미합니다.&lt;/p&gt;
&lt;p&gt;이처럼 도메인 이름과 DNS에 의한 계층 구조를 도입한 경우, 각 계층을 관리하는 관리자(레지스트리)가 필요합니다. 각 레지스트리는 다음과 같은 두 가지 책임이 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;자신이 맡게된(위임받은)존을 관리한다.&lt;/li&gt;
&lt;li&gt;존을 위임한 경우, 그 존을 위임받은 사람(위임처)이 누구인지를 관리한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;분산화된 이름인 도메인 이름과 DNS가 전체적으로 원활하게 작동하기 위해서는 각 범위를 관리하는 레지스트리와 그 관계자가 연계하고 협조해야 합니다.&lt;/p&gt;
&lt;h2&gt;Outro&lt;/h2&gt;
&lt;p&gt;지금까지 DNS가 왜 만들어졌고, 어떻게 작동하는지, 계층화와 위임을 통해 어떻게 전 세계적으로 분산 관리되고 있는지를 살펴봤습니다. 단순한 텍스트 파일 하나로 시작했던 &lt;code&gt;HOSTS&lt;/code&gt; 관리 방식에서 지금의 정교하고 확장성 높은 DNS 시스템으로 발전해 온 걸 보면, 인터넷의 성장과 함께 얼마나 큰 진화를 거쳤는지 실감할 수 있죠.&lt;/p&gt;
&lt;p&gt;DNS는 그저 도메인 이름을 IP주소로 바꿔주는 도구가 아닙니다. 전 세계의 기기들이 문제없이 통신할 수 있게 해주는 인터넷의 뼈대 같은 존재인 것 같습니다. 특히 계층 구조와 위임이라는 설계 철학 덕분에, 이 방대한 시스템이 유연하면서도 안정적으로 운영될 수 있었습니다. 결국 DNS가 잘 작동하기 위해선 각 계층의 관리자들이 제 역할을 해내고, 서로 협력하는 게 중요합니다. 이걸 보면 DNS는 단순히 기술을 넘어서, 일종의 사회적 약속이자 협업 시스템이라고도 할 수 있겠습니다.&lt;/p&gt;
&lt;p&gt;다음 글에선 레지스트리에 대해 더 깊게 살펴보고 레지스트리⋅레지스트라 모델, 도메인 이름의 등록과 사용에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=282893007&amp;amp;partner=openAPI&amp;amp;start=api&quot;&gt;DNS 실전 교과서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>DNS</category><category>NameServer</category><author>Jinmu Go</author></item><item><title>DNS (Domain Name System) DeepDive - 레지스트리, 레지스트라</title><link>https://jinmugo.com/posts/dns-deepdive-2/</link><guid isPermaLink="true">https://jinmugo.com/posts/dns-deepdive-2/</guid><description>도메인 이름을 등록하고 운영하는 레지스트리와 레지스트라의 역할, 관리 모델, 네임서버 위임 과정을 설명합니다.</description><pubDate>Sat, 28 Jun 2025 11:46:25 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;The Internet is for Everyone&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;지난 글에서 DNS의 배경과 시스템 그리고 도메인 이름에 대해 알아 보았습니다. 이번 글에서는 도메인 이름과 DNS를 원활히 동작하게 하기 위한 &lt;strong&gt;도메인 이름의 등록 관리 구조와 관리 체계&lt;/strong&gt;에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h2&gt;레지스트리란?&lt;/h2&gt;
&lt;p&gt;인터넷은 전체를 하나의 기관이 집중 관리하지 않는 분산 관리가 기본입니다.
그러나 인터넷에 연결된 호스트를 식별하기 위한 IP 주소나 도메인 이름과 같은 식별자는 예외적으로, 일원화된 관리를 해야 합니다. 그러한 일을 하는 조직을 &lt;strong&gt;레지스트리(registry)&lt;/strong&gt; 라고 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!note] 레지스트리와 레지스트리 오퍼레이터
&quot;레지스트리&quot;라는 용어는 아래 두 가지 의미로 사용됩니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;식별자를 할당하고 등록 관리를 하는 레지스트리 오퍼레이터&lt;/li&gt;
&lt;li&gt;레지스트리 오퍼레이터가 취급하는 등록대장인 레지스트리 데이터베이스.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 글에서는 1. 의 &lt;strong&gt;&apos;레지스트리 오퍼레이터&apos;를 레지스트리라고 지칭&lt;/strong&gt;합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;IP 주소와 도메인 이름 간 관리의 차이&lt;/h3&gt;
&lt;p&gt;IP주소와 도메인 이름은 각각의 특징으로 인해 할당 및 등록 관리에 대한 처리가 다릅니다.
IP 주소는 한정된 자원을 인터넷 전체에서 공유하기 때문에 세계적으로 &lt;strong&gt;일관성 있는 관리&lt;/strong&gt;를 해야 합니다.
이를 위해 IP 주소 레지스트리는 규칙을 정하거나 레지스트리 간의 조정을 담당합니다.
반면, 도메인 이름은 하나의 네임 스페이스를 TLD마다 분할하고, &lt;strong&gt;각 TLD의 특색에 맞게 운영할 수 있는 유연성을&lt;/strong&gt; 가집니다.&lt;/p&gt;
&lt;h3&gt;레지스트리의 역할&lt;/h3&gt;
&lt;p&gt;도메인 이름을 사용하기 위해서는 레지스트리에게 등록 신청을 해야합니다. 이후 신청을 접수한 레지스트리는 그 내용이 등록 요건에 부합하는지를 심사 및 확인하고 데이터베이스에 등록합니다.&lt;/p&gt;
&lt;p&gt;레스트리의 주된 역할은 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;레지스트리 데이터베이스의 운용 관리
&lt;ul&gt;
&lt;li&gt;도메인 이름을 등록하기 위해 필요한 개인의 이름, 조직명, 연락처와 같은 정보를 저장하는 &lt;strong&gt;레지스트리 데이터베이스&lt;/strong&gt;를 운용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정책에 따른 등록 규칙의 제정
&lt;ul&gt;
&lt;li&gt;레지스트리는 자신이 등록 관리하는 도메인 이름의 &lt;strong&gt;정책&lt;/strong&gt;을 정합니다. 그리고 그 정책을 실현하기 위한 &lt;strong&gt;등록 규칙&lt;/strong&gt;을 정하고 이용자에게 알립니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;등록 신청 접수
&lt;ul&gt;
&lt;li&gt;레지스트리는 등록자로부터 도메인 이름의 등록 신청을 접수합니다. 신청된 도메인 이름을 규칙에 따라 심사하고 접수한 정보를 레지스트리 데이터베이스에 등록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Whois 서비스 제공
&lt;ul&gt;
&lt;li&gt;레지스트리는 자신이 관리하는 도메인 이름의 정보를 &lt;code&gt;Whois&lt;/code&gt;서비스로 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;네임서버 운용
&lt;ul&gt;
&lt;li&gt;관리하는 도메인들을 인터넷에서 이용할 수 있도록 &lt;strong&gt;네임 서버&lt;/strong&gt;를 관리 및 운용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;레지스트리와 TLD의 관계&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;TLD(Top Level Domain)&lt;/code&gt;마다 레지스트리가 존재하며, 각 레지스트리 TLD를 관리하고 관리 정책이나 등록 규칙을 정합니다.&lt;/p&gt;
&lt;p&gt;TLD는 크게 두 종류로 나뉩니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;국가나 지역마다 할당되는 도메인 &lt;code&gt;ccTLD(Country Code Top Level Domain)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;ex) &lt;code&gt;.kr&lt;/code&gt;, &lt;code&gt;.jp&lt;/code&gt;, &lt;code&gt;.cn&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;국가나 지역에 상관없는 도메인 &lt;code&gt;gTLD(Generic Top Level Domain)&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;ex) &lt;code&gt;.com&lt;/code&gt;, &lt;code&gt;.net&lt;/code&gt;, &lt;code&gt;.org&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;레지스트리⋅레지스트라 모델과 레지스트라의 역할&lt;/h2&gt;
&lt;p&gt;도메인 이름을 사용하기 위해서는 그 도메인 이름을 관리하는 레지스트리의 레지스트리 데이터베이스에 등록해야 합니다.&lt;/p&gt;
&lt;p&gt;오늘날 주요한 TLD(.com/.net/.kr)는 &lt;strong&gt;레지스트리⋅레지스트라 모델(registry⋅registrar model)&lt;/strong&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;이렇게 역할을 분리하는 이유는 등록될 도메인의 이름을 고유하게 하면서 가격과 서비스의 다양성을 확보하기 위해서입니다. 그래서 하나의 TLD에 대해서 레지스트리는 하나이지만 레지스트라는 여럿이 존재합니다.&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;ol&gt;
&lt;li&gt;등록자로부터 등록 신청 접수&lt;/li&gt;
&lt;li&gt;레지스트리 데이터베이스에 등록 의뢰&lt;/li&gt;
&lt;li&gt;whois 서비스 제공&lt;/li&gt;
&lt;li&gt;등록자 정보 관리&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;도메인 이름 등록하기&lt;/h2&gt;
&lt;p&gt;레지스트리⋅레지스트라 모델에서 도메인 이름을 등록하는 흐름을 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;신청자&lt;/strong&gt;는 도메인 이름이 등록 가능한지 &lt;code&gt;whois&lt;/code&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;/p&gt;
&lt;p&gt;레지스트리는 레지스트라로부터 받은 신청이 타당한지 확인하고 등록 요건을 충족한다면, 데이터베이스에 정보를 등록합니다. 이후 레지스트라에게 등록이 완료되었음을 알립니다. 알림을 받은 레지스트라는 사용자에게 도메인 이름이 등록완료되었음을 알립니다.&lt;/p&gt;
&lt;h2&gt;도메인 이름 사용하기&lt;/h2&gt;
&lt;p&gt;위에서 사용자는 도메인 이름을 등록했습니다. 그러나 등록만 해서는 도메인 이름을 사용할 수 없습니다. 도메인 이름을 가진 웹사이트에 웹 브라우저로 접속할 수 있도록 하게 하기 위해서는 몇가지 준비가 필요합니다.&lt;/p&gt;
&lt;p&gt;첫째, 그 도메인 이름을 인터넷에서 사용할 수 있도록 하기 위한 정보, 즉 DNS 정보를 관리하는 &lt;strong&gt;네임 서버&lt;/strong&gt;를 등록해야 합니다. 네임 서버는 직접 구축하거나 cloudflare같은 외부 서비스를 이용할 수 있습니다. 또한 네임 서버는 인터넷의 어디에서라도 접근할 수 있어야 합니다.&lt;/p&gt;
&lt;p&gt;둘째, 처음에 구축한 네임 서버에 도메인 이름의 정보를 설정합니다. 만약 등록한 도메인 이름이 &lt;code&gt;jinmu.me&lt;/code&gt;라면 &lt;code&gt;jinmu.me&lt;/code&gt;의 IP 주소는 &lt;code&gt;76.76.21.21&lt;/code&gt;이라는 정보가 네임 서버에 설정됩니다.&lt;/p&gt;
&lt;p&gt;마지막은 이렇게 등록한 정보를 기반으로 네임 서버가 인터넷에서 온 질문에 대해 대답할 수 있는지 확인하는 겁니다. 예를 들어, &lt;code&gt;jinmu.me&lt;/code&gt;의 IP주소는? 이라는 질문에 대해 &lt;code&gt;76.76.21.21&lt;/code&gt;이라고 답할 수 있어야 합니다.&lt;/p&gt;
&lt;h3&gt;레지스트리가 하는 일&lt;/h3&gt;
&lt;p&gt;레지스트리는 네임 서버 정보를 레지스트리 데이터베이스에 등록하고, 레지스트리가 관리하는 네임 서버에 그 정보를 설정합니다. &lt;code&gt;jinmu.me&lt;/code&gt;의 정보를 담고 있는 네임 서버 정보를 레지스트리가 관리하는 ME 존의 네임 서버에 설정합니다. 부모(me)에게 자식(jinmu.me)의 네임 서버 정보가 설정되어 레지스트리와 등록자의 관계는 DNS에서 말하는 부모-자식 관계가 됩니다.&lt;/p&gt;
&lt;p&gt;위의 절차를 완료하면 실제로 웹 서버를 구축해 그 도메인 이름(jinmu.me)을 가진 웹사이트에 실제로 접근할 수 있는지 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Outro&lt;/h2&gt;
&lt;p&gt;이번 글에서는 도메인 이름이 원활하게 작동하기 위해 필요한 등록 및 관리 구조에 대해 자세히 알아보았습니다. 인터넷 식별자를 일원화하여 관리하는 &lt;strong&gt;레지스트리&lt;/strong&gt;의 역할부터, 사용자가 도메인을 등록할 수 있도록 중개하는 &lt;strong&gt;레지스트라&lt;/strong&gt;의 기능까지 살펴보았죠. 특히 오늘날 대부분의 TLD가 채택하고 있는 &lt;strong&gt;레지스트리-레지스트라 모델&lt;/strong&gt;이 어떻게 도메인 이름의 안정적인 관리와 서비스 및 가격 경쟁을 동시에 가능하게 하는지, 그리고 실제 도메인 등록 절차가 어떻게 이루어지는지 확인했습니다. 마지막으로, 단순히 도메인을 등록하는 것을 넘어 네임 서버를 설정하고 부모 도메인에 해당 정보가 등록되어야만 비로소 인터넷에서 도메인을 사용할 수 있게 된다는 점도 짚어보았습니다.&lt;/p&gt;
&lt;p&gt;다음 글에서는 이렇게 등록되고 설정된 도메인 이름을 어떻게 우리의 컴퓨터가 찾아가는지, 즉 &lt;strong&gt;&apos;DNS 이름 풀이(Name Resolution)&apos;&lt;/strong&gt; 과정에 대해 다뤄보겠습니다. 또한, 이 거대한 시스템이 원활하게 동작할 수 있도록 하는 핵심 원리인 &lt;strong&gt;&apos;위임(Delegation)&apos;&lt;/strong&gt; 이 왜 중요한지에 대해서도 설명합니다.&lt;/p&gt;
&lt;p&gt;감사합니다.&lt;/p&gt;
</content:encoded><category>DNS</category><category>Registry</category><category>Registrar</category><author>Jinmu Go</author></item><item><title>DNS (Domain Name System) DeepDive - Name Resolution</title><link>https://jinmugo.com/posts/dns-deepdive-3/</link><guid isPermaLink="true">https://jinmugo.com/posts/dns-deepdive-3/</guid><description>DNS 질의가 계층형 네임서버를 따라 이름을 해석하는 과정과 재귀·반복 질의, 캐시로 부하와 응답 시간을 줄이는 원리를 설명합니다.</description><pubDate>Sun, 06 Jul 2025 02:48:10 GMT</pubDate><content:encoded>&lt;p&gt;이용자의 요구에 따라 도메인 이름에 대응하는 IP주소를 찾아내는 것을 &lt;strong&gt;Name Resolution(이름풀이)&lt;/strong&gt; 라고 합니다. 이번 포스팅에서는 DNS에 의한 이름 풀이의 기본적인 동작을 확인할 예정입니다.&lt;/p&gt;
&lt;h2&gt;질의와 응답&lt;/h2&gt;
&lt;p&gt;Name Resolution의 기본은 &lt;strong&gt;질의&lt;/strong&gt;와 &lt;strong&gt;응답&lt;/strong&gt;입니다. 이 질의와 응답에는 세 가지 약속이 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;질의와 응답은 항상 1대1로 대응한다.
&lt;ul&gt;
&lt;li&gt;질의에 대응하지 않는 응답이 오거나, 한 개의 질의에 두 개의 응답이 오는 일은 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정보를 알고 싶은 사람은 알고 싶은 정보의 도메인 이름과 타입을 지정해서 질의한다.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jinmu.me의 IP 주소&lt;/code&gt;와 같이 이름과 종류를 함께 지정해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정보를 제공하는 사람은 받은 질의에 대해 자신이 알고 있는 정보를 응답한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;계층 구조를 따르는 Name Resolution&lt;/h2&gt;
&lt;p&gt;이전 포스팅에서 설명드렸던 것과 같이 DNS에서는 계층화와 위임에 의해 루트를 정점으로 갖는 계층 구조가 만들어집니다. DNS에 의한 이름 풀이의 기본은 정점인 루트의 네임 서버에서부터 계층의 순서를 따라가, 알고 싶은 정보를 질의하여 필요한 정보를 입수하고 최종 목적인 IP주소를 얻는 것입니다. DNS에서는 이런 행위를 &lt;strong&gt;iterative resolution(반복에 의한 풀이)&lt;/strong&gt; 라고 합니다. 앞으로 이런 동작(iterative resolution)을 &lt;strong&gt;계층 구조를 따라간다&lt;/strong&gt;고 표현하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;정보를 알고싶은 사람이 DNS의 계층 구조를 따라 Name Resolution을 하기 위해선 적절한 질의처를 알아야 합니다. 그래서 DNS에서는 자식의 네임 서버 정보를 부모에게 등록하고, 위임한 도메인 이름에 대한 질의를 받은 부모는 다음 질의처로 자식의 네임 서버 정보를 응답하는 구조를 채택했습니다. 이 구조 덕분에 질의자는 위임에 의한 계층 구조를 따라갈 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;사전 준비(그림 왼쪽)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;루트&lt;/code&gt;와 &lt;code&gt;me&lt;/code&gt;사이엔 위임에 의한 부모-자식 관계가 성립되어 있다. 루트가 부모, &lt;code&gt;me&lt;/code&gt;가 자식이 된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;me&lt;/code&gt;은 &lt;code&gt;부모&lt;/code&gt;인 루트에게 자신의 네임 서버 정보를 등록한다. &lt;code&gt;루트&lt;/code&gt;는 &lt;code&gt;me&lt;/code&gt;이 등록한 네임 서버 정보를 자신의 네임 서버에 등록 및 설정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;실제 이름 풀이 (그림 오른쪽)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;위임에 의한 부모-자식 관계가 성립된 상태에서 A가 루트에게 &apos;jinmu.me의 IP주소를 알려주세요&apos;라고 질의한다.&lt;/li&gt;
&lt;li&gt;루트는 &lt;code&gt;me&lt;/code&gt;에 대해서는 위임처의 네임 서버 정보만 알고 있기에 &lt;code&gt;me&lt;/code&gt;의 네임 서버 정보(&lt;code&gt;b.dns.me&lt;/code&gt;)를 응답한다.&lt;/li&gt;
&lt;li&gt;A는 루트로부터 받은 me의 네임 서버 정보를 사용해 &lt;code&gt;b.dns.me&lt;/code&gt;에게 질의한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 DNS의 계층 구조를 루트에서부터 순서대로 따라가는 것이 이름 풀이 구조의 기본입니다.&lt;/p&gt;
&lt;h2&gt;이름 풀이의 작동&lt;/h2&gt;
&lt;p&gt;위에서 살펴보았던 계층 구조에 따른 이름 풀이를 조금 더 자세히 살펴 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;질의&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User는 계층 구조의 정점인 루트의 네임 서버로 알고 싶은 정보의 이름과 종류를 지정해서 질의를 보냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;응답&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;루트는 &lt;code&gt;me&lt;/code&gt;를 위임하고 있기 때문에 &lt;code&gt;me&lt;/code&gt;의 네임 서버 정보(이 예시에서는 &lt;code&gt;b.dns.me&lt;/code&gt;)를 응답합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;질의&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;루트로부터 받은 응답에서 아래 두 가지를 알 수 있습니다.
&lt;ul&gt;
&lt;li&gt;루트는 &lt;code&gt;me&lt;/code&gt;을 위임하고 있다.&lt;/li&gt;
&lt;li&gt;위임처의 네임 서버는 &lt;code&gt;b.dns.me&lt;/code&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 정보를 이용해 &lt;code&gt;me&lt;/code&gt;의 네임 서버로 1번의 질의를 보냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;응답&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;me&lt;/code&gt;은 &lt;code&gt;jinmu.me&lt;/code&gt;을 위임하고 있기 때문에 위임처인 &lt;code&gt;jinmu.me&lt;/code&gt;의 네임 서버 정보(이 예시 에서는 &lt;code&gt;ns1.jinmu.me&lt;/code&gt;)를 응답합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;질의&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;me&lt;/code&gt;로부터 받은 응답에서 아래 두 가지를 알 수 있습니다.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;me&lt;/code&gt;는 &lt;code&gt;jinmu.me&lt;/code&gt;을 위임하고 있다.&lt;/li&gt;
&lt;li&gt;위임처의 네임 서버는 &lt;code&gt;ns1.jinmu.me&lt;/code&gt;이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 정보를 이용해 &lt;code&gt;jinmu.me&lt;/code&gt;의 네임 서버로 1번과 똑같은 질의를 보냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;응답&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jinmu.me&lt;/code&gt;의 네임 서버는 &lt;code&gt;jinmu.me&lt;/code&gt;의 IP주소를 알고 있기 때문에 IP 주소를 응답합니다. 이것으로 User는 &lt;code&gt;jinmu.me&lt;/code&gt;의 IP 주소(&lt;code&gt;76.76.21.21&lt;/code&gt;)를 얻을 수 있으며 이름 풀이가 종료됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;이름 풀이의 부하와 시간의 경감&lt;/h2&gt;
&lt;p&gt;이처럼 루트에서부터 DNS의 계층 구조를 따라가 이름 풀이를 하는 것은 확실하지만, 많은 요청과 응답 그리고 시간이 듭니다. 그래서 이름 풀이를 담당하는 별도의 서버를 두고 다른 호스트로부터 의뢰를 접수해 이름 풀이를 대행하는 방법이 고안되었습니다.&lt;/p&gt;
&lt;p&gt;예시에서 User대신에 이름 풀이를 해주는 대행자를 X라 하겠습니다. 이때 User는 X에게 &apos;&lt;strong&gt;나 대신 이름 풀이를 해주고&lt;/strong&gt; &lt;code&gt;jinmu.me&lt;/code&gt;의 IP주소를 알려주세요&apos;라는 질의를 보내며, 위에서 설명한 이름 풀이는 X가 대행하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;X는 A가 부탁한 이름 풀이를 하여 &lt;code&gt;jinmu.me&lt;/code&gt;의 IP주소 &lt;code&gt;76.76.21.21&lt;/code&gt;를 User에게 응답합니다.&lt;/p&gt;
&lt;p&gt;그리고 X는 이름 풀이를 할 때 질의처인 네임 서버로부터 얻은 정보를 일정 시간 저장합니다. 만약 시간 내에 A가 같은 질의를 X에게 했을 때, X는 이미 그 결과를 가지고 있기 때문에 새로운 이름 풀이는 하지 않고 가지고 있는 결과를 User에게 응답합니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;이렇게 되면 네임 서버에 질의하는 횟수를 줄일 수 있어 이름 풀이에 걸리는 부하와 시간을 경감할 수 있습니다.&lt;/p&gt;
&lt;p&gt;지금 까지의 정보를 통해 우리는 DNS이름 풀이를 할 때의 역할에는 세 종류가 있음을 알 수 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;정보가 필요한 사람(User A, User B, User C)&lt;/li&gt;
&lt;li&gt;정보가 필요한 사람으로부터 의뢰를 받아 이름 풀이를 하는 사람(X)&lt;/li&gt;
&lt;li&gt;정보를 제공하는 사람(DNS의 계층 구조를 만드는 네임 서버들)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Outro&lt;/h2&gt;
&lt;p&gt;이번 글에서는 도메인 이름에 해당하는 IP 주소를 찾아가는 과정인 &lt;strong&gt;&apos;이름 풀이(Name Resolution)&apos;&lt;/strong&gt; 에 대해 살펴보았습니다. 루트 네임 서버에서 시작해 계층 구조를 순서대로 따라가는 &lt;strong&gt;반복적인 질의(iterative resolution)&lt;/strong&gt; 가 기본 원리라는 것과, 이 과정의 질의⋅응답 부하와 시간을 줄이기 위해 캐시 기능을 갖춘 이름 풀이 대행자가 등장했습니다. 이를 통해 DNS 생태계가 정보를 요청하는 &apos;사용자&apos;, 대신해서 이름 풀이를 실행하는 &apos;캐시 리졸버&apos;, 그리고 정보를 제공하는 &apos;권한 네임 서버&apos;라는 세 가지 역할로 구성된다는 점을 확인 할 수 있었습니다.&lt;/p&gt;
&lt;p&gt;지금까지 DNS가 &apos;어떻게&apos; 동작하는지에 대한 큰 그림을 그려보았습니다. 다음 포스팅에서는 한 걸음 더 나아가, 이 과정을 실제로 가능하게 하는 DNS의 구체적인 구성 요소들은 무엇이며, 각 요소가 어떤 정보를 주고받으며 작동하는지에 대해 더 자세히 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;감사합니다.&lt;/p&gt;
</content:encoded><category>DNS</category><author>Jinmu Go</author></item><item><title>첫 15km 완주!</title><link>https://jinmugo.com/posts/first-15km-running/</link><guid isPermaLink="true">https://jinmugo.com/posts/first-15km-running/</guid><description>풀 마라톤을 향한 첫 걸음.</description><pubDate>Sun, 27 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;가볍게 5km만 뛰러 나갔는데 날씨도 좋고. 듣고 있는 팟캐스트도 재밌어서 더 뛰었다.
나중 갈수록 다리가 뻐근했지만, 이정도면 하프마라톤은 뛸 수 있겠다 생각했다.&lt;/p&gt;
&lt;p&gt;![[IMG_2562.jpg]]&lt;/p&gt;
&lt;p&gt;달리는 동안 지루하지 않게 해준 감사한 팟캐스트들!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://podcasts.apple.com/kr/podcast/ep57-4%EC%9B%94%EC%9D%98-%EB%B0%9C%EA%B2%AC-%EC%98%A4%EB%9E%98-%EB%8B%A4%EC%A0%95%ED%95%98%EB%A0%A4%EB%A9%B4-%EA%B0%80%EB%81%94-%EB%B3%84%EB%A1%9C%EC%97%AC%EC%95%BC-%ED%95%B4/id1738806607?i=1000705022558&amp;amp;l=en-GB&quot;&gt;무소속 생활자 57화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://podcasts.apple.com/kr/podcast/%EA%B9%80%ED%98%9C%EB%A6%AC%EC%9D%98-%ED%95%84%EB%A6%84%ED%81%B4%EB%9F%BD/id1188513056?i=1000617803118&quot;&gt;필름클럽 186화&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>marathon</category><author>Jinmu Go</author></item><item><title>2025 춘천마라톤 첫번째 풀 마라톤 완주</title><link>https://jinmugo.com/posts/first-full-marathon/</link><guid isPermaLink="true">https://jinmugo.com/posts/first-full-marathon/</guid><description>죽는줄 알았습니다.</description><pubDate>Mon, 27 Oct 2025 05:32:08 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;2025 춘천마라톤 풀코스 완주를 했다. 나름 오랫동안 꾸준히 러닝을 해서 비교적 쉽게 완주할줄 알았지만 마지막 40~42km 구간이 그 어느 때보다 멀게 느껴졌다.&lt;/p&gt;
&lt;p&gt;연습했을 때는 32km까지만 뛰고 마라톤에 참여한 터라 32 ~ 42km 의 10km구간은 미지의 구간이었는데 기록보니 나름 안정적으로 주파한 것 같아서 다행이었다. 아마 20km까지 페이스 메이커의 도움을 받아 가면서 뛴 거라 더 괜찮았던 것 같다.&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;
</content:encoded><category>marathon</category><category>running</category><author>Jinmu Go</author></item><item><title>스플릿 키보드 입문기</title><link>https://jinmugo.com/posts/first-split-keyboard/</link><guid isPermaLink="true">https://jinmugo.com/posts/first-split-keyboard/</guid><description>Corne v4.1 스플릿 키보드를 2주간 사용하며 느낀 어깨 부담, 휴대성, 레이어 적응과 무선 연결의 장단점 및 키맵을 기록했습니다.</description><pubDate>Sun, 27 Jul 2025 11:13:28 GMT</pubDate><content:encoded>&lt;p&gt;최근 코딩을 하면서 오래 앉아 타이핑을 하다 보니 어깨가 많이 결렸다. 2~3년 전 키보드에 한창 관심이 많았을 때 스플릿 키보드에 대해 언뜻 들었던 기억이 났고, 이번 기회에 한 번 사용해보기로 결심했다.&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;ol&gt;
&lt;li&gt;&lt;strong&gt;휴대성&lt;/strong&gt;: 들고 다닐 수 있을 정도로 컴팩트해야 함&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;미니멀한 배열&lt;/strong&gt;: 키 수가 적을수록 좋음&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;연결 방식&lt;/strong&gt;: 유무선 모두 지원하되, 무선을 선호&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;장식 요소&lt;/strong&gt;: LED 등은 불필요&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;휴대하면서 사용하고 싶었기 때문에 처음 적응이 어렵더라도 위 조건들을 만족하는 키보드를 찾았다.
인터넷을 돌아다니면서 찾아보니, 입문용으로 &lt;strong&gt;Corne v4.1&lt;/strong&gt;를 많이 구매하는 것 같아서 이 모델을 선택했다. 내 조건에 부합하면서도 무난한 선택지라고 생각한다.&lt;/p&gt;
&lt;p&gt;현재 사용한지 약 2주가 지났다. 처음 며칠은 확실히 적응이 필요했지만, 지금은 상당히 만족스럽게 사용하고 있다. 그동안의 경험을 장/단점으로 나누어보자면&lt;/p&gt;
&lt;h2&gt;장점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;어깨 결림 개선:&lt;/strong&gt; 먼저 가장 큰 구매 목적이었던 어깨 결림이 어느정도 개선되었다. 확실히 양손을 자연스러운 위치에 둘 수 있어서 어깨와 팔의 긴장이 많이 줄었다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;휴대성:&lt;/strong&gt; 작고 가벼워서 가방에 넣고 다니기 편하다. 카페나 다른 장소에서 작업할 때도 부담 없이 가져갈 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;책상 공간 활용&lt;/strong&gt;: 기존 키보드보다 훨씬 적은 공간을 차지한다. 특히 양쪽을 원하는 거리만큼 벌려서 사용할 수 있어 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;단점&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;특수문자 입력의 불편함&lt;/strong&gt;: 일반 키보드에 비해 특수문자를 입력하는 것이 여전히 어색하다. 레이어를 바꿔가며 입력해야 하는 문자들이 많아서 아직 완전히 익숙하지 않다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;무선 연결 불안정&lt;/strong&gt;: 가끔씩 무선 연결이 끊어지는 경우가 있다. 유선으로도 사용할 수 있지만, 무선의 편리함을 포기하기엔 아쉽다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;소음 문제&lt;/strong&gt;:  타이핑 소음이 생각보다 크다.  아무래도 기체자체가 작으니 그런 것 같다 스위치를 바꾸면 어떨까 싶어 저소음 스위치를 주문한 상태다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;총평&lt;/h2&gt;
&lt;p&gt;2주간 사용해본 결과, 전반적으로 매우 만족스럽다. 초기 목적이었던 어깨 결림 개선은 확실히 효과를 보고 있고, 휴대성과 공간 활용 측면에서도 기대 이상이다.&lt;/p&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;pre&gt;&lt;code&gt;title: &quot;GitHub - JinmuGo/zmk-config-corne&quot;
image: &quot;https://opengraph.githubassets.com/9d321906a805d9c45f9c7ec68bacef10b51c149ae108081c5a19f65e1afd0f97/JinmuGo/zmk-config-corne&quot;
description: &quot;Contribute to JinmuGo/zmk-config-corne development by creating an account on GitHub.&quot;
url: &quot;https://github.com/JinmuGo/zmk-config-corne?tab=readme-ov-file&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 레포에서 조금 더 최신 키맵을 살펴볼 수 있다.&lt;/p&gt;
</content:encoded><category>keyboard</category><category>splitKeyboard</category><author>Jinmu Go</author></item><item><title>Git Squash와 Fixup 차이, 언제 무엇을 쓸까</title><link>https://jinmugo.com/posts/git-squash-vs-fixup/</link><guid isPermaLink="true">https://jinmugo.com/posts/git-squash-vs-fixup/</guid><description>squash는 커밋 메시지를 합쳐 편집하고 fixup은 보완 커밋의 메시지를 버립니다. --fixup과 --autosquash를 이용한 안전한 정리 흐름도 함께 설명합니다.</description><pubDate>Fri, 11 Jul 2025 01:13:41 GMT</pubDate><content:encoded>&lt;p&gt;둘 다 현재 커밋을 앞선 커밋에 합치지만 차이는 &lt;strong&gt;커밋 메시지 처리&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;squash&lt;/code&gt;: 두 커밋의 메시지를 모아 보여주고 최종 메시지를 편집합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fixup&lt;/code&gt;: 보완 커밋의 메시지를 버리고 대상 커밋의 메시지를 그대로 유지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이미 설명이 완성된 커밋에 오타나 누락을 보태는 작업이라면 &lt;code&gt;fixup&lt;/code&gt;이 맞습니다. 여러 커밋의 의미를 하나로 재작성해야 한다면 &lt;code&gt;squash&lt;/code&gt;를 씁니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;squash&lt;/h2&gt;
&lt;p&gt;Interactive rebase 목록에서 &lt;code&gt;squash&lt;/code&gt; 또는 &lt;code&gt;s&lt;/code&gt;로 바꾼 커밋은 바로 위 커밋에 합쳐집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pick   9ecdf8 refactor: tmux config
squash 8d5218 refactor: cleanup zsh config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Git은 합쳐질 커밋 메시지들을 에디터에 열어 줍니다. 최종 커밋이 두 작업을 모두 설명해야 하거나 메시지를 새로 정리하고 싶을 때 유용합니다.&lt;/p&gt;
&lt;p&gt;대상 커밋을 미리 지정해 보완 커밋을 만들 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --squash=&amp;lt;target-commit&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 커밋은 &lt;code&gt;squash!&lt;/code&gt; 접두사를 가지며, 뒤에서 설명할 &lt;code&gt;--autosquash&lt;/code&gt;가 대상 위치로 옮겨 줍니다.&lt;/p&gt;
&lt;h2&gt;fixup&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;fixup&lt;/code&gt; 또는 &lt;code&gt;f&lt;/code&gt;도 변경 내용을 바로 위 커밋에 합칩니다. 기본 &lt;code&gt;fixup&lt;/code&gt;은 보완 커밋의 메시지를 버리므로 메시지 에디터를 다시 열 필요가 없습니다.&lt;/p&gt;
&lt;p&gt;제가 lazygit 설정 커밋 &lt;code&gt;3f05b2a&lt;/code&gt;의 누락을 발견했다고 해보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;수정 파일을 스테이징한 뒤 Git이 제공하는 &lt;code&gt;--fixup&lt;/code&gt; 옵션으로 커밋을 만듭니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add lazygit/
git commit --fixup=3f05b2a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Git이 &lt;code&gt;fixup! feat: add lazygit config&lt;/code&gt; 같은 제목을 정확히 만들어 줍니다. 접두사를 손으로 작성할 수도 있지만 대상 제목이 바뀌거나 비슷한 커밋이 있을 때 실수하기 쉬우므로 &lt;code&gt;git commit --fixup=&amp;lt;commit&amp;gt;&lt;/code&gt;을 쓰는 편이 안전합니다.&lt;/p&gt;
&lt;h2&gt;autosquash로 대상 위치에 붙이기&lt;/h2&gt;
&lt;p&gt;보완 커밋은 작업 중인 HEAD에 만들어져 대상과 떨어져 있을 수 있습니다. &lt;code&gt;--autosquash&lt;/code&gt;는 &lt;code&gt;fixup!&lt;/code&gt;, &lt;code&gt;squash!&lt;/code&gt;, &lt;code&gt;amend!&lt;/code&gt; 커밋을 찾아 대상 바로 뒤로 이동하고 rebase 액션도 알맞게 바꿉니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git rebase -i --autosquash &amp;lt;base&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;base&amp;gt;&lt;/code&gt;에는 정리할 범위의 바로 앞 커밋을 지정합니다. 예시의 대상부터 정리하려면 다음처럼 실행할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git rebase -i --autosquash 3f05b2a^
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;목록을 확인하고 저장하면 보완 변경은 대상 커밋에 합쳐지고 &lt;code&gt;fixup!&lt;/code&gt; 커밋은 사라집니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;매번 옵션을 입력하기 싫다면 Git의 기본 설정을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --global rebase.autoSquash true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 설정 뒤에도 &lt;code&gt;git rebase -i &amp;lt;base&amp;gt;&lt;/code&gt;의 목록을 저장하기 전에는 배치를 직접 확인할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;메시지도 고치고 싶을 때&lt;/h2&gt;
&lt;p&gt;최신 Git은 fixup 계열에서 메시지 교체까지 표현할 수 있습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;명령&lt;/th&gt;
&lt;th&gt;결과&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git commit --fixup=&amp;lt;commit&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;변경을 합치고 기존 대상 메시지를 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git commit --fixup=amend:&amp;lt;commit&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;변경을 합치며 대상 메시지의 수정안을 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git commit --fixup=reword:&amp;lt;commit&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;파일 변경 없이 대상 메시지만 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;amend:&lt;/code&gt;와 &lt;code&gt;reword:&lt;/code&gt;는 &lt;code&gt;amend!&lt;/code&gt; 커밋을 만들고, &lt;code&gt;--autosquash&lt;/code&gt; rebase 때 그 메시지를 대상 커밋의 새 메시지로 사용합니다. 단순 보완에는 기본 &lt;code&gt;--fixup&lt;/code&gt;이면 충분합니다.&lt;/p&gt;
&lt;h2&gt;한눈에 비교&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기준&lt;/th&gt;
&lt;th&gt;&lt;code&gt;squash&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;fixup&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;변경 내용&lt;/td&gt;
&lt;td&gt;이전 커밋에 합침&lt;/td&gt;
&lt;td&gt;이전 커밋에 합침&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;메시지&lt;/td&gt;
&lt;td&gt;여러 메시지를 모아 편집&lt;/td&gt;
&lt;td&gt;보완 메시지를 버리고 대상 메시지 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주 용도&lt;/td&gt;
&lt;td&gt;여러 작업의 의미를 하나로 재작성&lt;/td&gt;
&lt;td&gt;기존 커밋의 작은 누락·오타·리뷰 반영&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;빠른 생성&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git commit --squash=&amp;lt;commit&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git commit --fixup=&amp;lt;commit&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;안전하게 쓰는 범위&lt;/h2&gt;
&lt;p&gt;Rebase는 커밋 ID를 다시 만듭니다. 아직 공유하지 않은 로컬 브랜치에서 정리하는 것이 가장 안전합니다.&lt;/p&gt;
&lt;p&gt;이미 원격에 올린 개인 브랜치를 꼭 고쳐야 한다면 팀원과 먼저 조율하고, 테스트 후 일반 &lt;code&gt;--force&lt;/code&gt; 대신 다음 명령을 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push --force-with-lease
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--force-with-lease&lt;/code&gt;도 다른 사람이 같은 브랜치에서 작업 중일 때는 충돌을 없애 주지 않습니다. 공유 브랜치와 기본 브랜치의 공개 이력은 다시 쓰지 않는 편이 낫습니다.&lt;/p&gt;
&lt;p&gt;제가 가장 자주 쓰는 흐름은 짧습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit --fixup=&amp;lt;target&amp;gt;
git rebase -i --autosquash &amp;lt;base&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;작업 중에는 보완점을 바로 fixup 커밋으로 남기고, 리뷰 전 한 번에 정리하면 메시지를 반복해서 고치는 수고 없이 읽기 좋은 이력을 만들 수 있습니다.&lt;/p&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-commit&quot;&gt;git commit 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/docs/git-rebase&quot;&gt;git rebase 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>git</category><author>Jinmu Go</author></item><item><title>HTTP/1.1 파이프라이닝은 왜 사라졌을까</title><link>https://jinmugo.com/posts/http-pipelining/</link><guid isPermaLink="true">https://jinmugo.com/posts/http-pipelining/</guid><description>HTTP/1.1 파이프라이닝이 RTT를 줄이는 방식과 응답 순서 때문에 생기는 HOL 블로킹, HTTP/2·HTTP/3가 이를 어떻게 개선했는지 설명합니다.</description><pubDate>Thu, 16 Oct 2025 13:08:17 GMT</pubDate><content:encoded>&lt;p&gt;HTTP/1.1 파이프라이닝은 &lt;strong&gt;앞선 응답을 기다리지 않고 같은 연결에 여러 요청을 연속으로 보내 RTT 낭비를 줄이는 기능&lt;/strong&gt;입니다. 하지만 서버는 응답을 요청 순서대로 보내야 했고, 첫 응답이 늦으면 뒤의 빠른 응답도 막히는 애플리케이션 계층 HOL(Head-of-Line) 블로킹이 생겼습니다. 이 한계와 까다로운 오류 복구 때문에 널리 쓰이지 못했고, HTTP/2의 스트림 다중화가 그 역할을 대신했습니다.&lt;/p&gt;
&lt;h2&gt;HTTP/1.0: 기본은 요청마다 새 연결&lt;/h2&gt;
&lt;p&gt;HTTP/1.0의 기본 모델은 비영속 연결입니다. HTML, CSS, 이미지처럼 리소스를 하나 요청할 때마다 TCP 연결을 만들고 응답을 받은 뒤 닫습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;TCP 핸드셰이크와 느린 시작 비용이 요청마다 반복되므로 리소스가 많은 페이지에서는 지연이 커집니다. 당시 일부 구현은 &lt;code&gt;Keep-Alive&lt;/code&gt; 확장으로 연결을 재사용했지만, HTTP/1.0의 기본 동작은 아니었습니다.&lt;/p&gt;
&lt;h2&gt;HTTP/1.1: 연결을 재사용해도 대기는 남는다&lt;/h2&gt;
&lt;p&gt;HTTP/1.1은 별도 종료 지시가 없는 한 영속 연결을 기본으로 사용합니다. 한 번 만든 TCP 연결을 여러 요청에 재사용하므로 연결 설정 비용은 줄어듭니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;그렇다고 요청 사이의 대기가 자동으로 사라지는 것은 아닙니다. 요청 하나를 보내고 응답을 모두 받은 뒤 다음 요청을 보내면, 각 왕복 사이에 연결이 놀게 됩니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RTT(Round Trip Time)는 요청이 상대에게 갔다가 응답이 돌아오기까지 걸리는 왕복 시간입니다. TCP 연결 수립뿐 아니라 HTTP 요청과 응답의 지연을 설명할 때도 쓰입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;파이프라이닝의 동작&lt;/h2&gt;
&lt;p&gt;파이프라이닝을 사용하면 클라이언트는 첫 응답을 기다리지 않고 같은 연결에 요청을 이어서 씁니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트가 영속 TCP 연결을 만듭니다.&lt;/li&gt;
&lt;li&gt;요청 1, 2, 3을 응답 대기 없이 연속으로 보냅니다.&lt;/li&gt;
&lt;li&gt;서버는 여러 요청을 처리합니다.&lt;/li&gt;
&lt;li&gt;서버는 &lt;strong&gt;요청과 같은 순서로&lt;/strong&gt; 응답 1, 2, 3을 전송합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;여러 요청을 한 번의 왕복 구간에 밀어 넣을 수 있으므로 높은 지연 환경에서 이론적인 이득이 큽니다. 다만 메서드에 관한 규칙은 ‘멱등 메서드만 허용’처럼 한 문장으로 단순화하면 정확하지 않습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버는 안전한 메서드의 요청을 병렬 처리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;연결이 중간에 끊겼을 때 클라이언트가 자동 재시도해도 되는지는 멱등성에 달려 있습니다.&lt;/li&gt;
&lt;li&gt;클라이언트는 멱등하지 않은 요청을 보낸 뒤 최종 응답을 받기 전에는 추가 요청을 파이프라이닝하지 않는 것이 원칙입니다. 따라서 POST가 프로토콜 차원에서 무조건 금지되는 것은 아니지만, 복구가 어렵기 때문에 보수적으로 다뤄야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;응답 순서가 만든 HOL 블로킹&lt;/h2&gt;
&lt;p&gt;HTTP/1.1 메시지에는 여러 응답을 뒤섞어 보내고 다시 조립할 스트림 식별자가 없습니다. 그래서 서버는 요청을 병렬로 처리했더라도 응답을 요청 순서대로 보내야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. GET /api/complex-report  → 5초
2. GET /images/logo.png     → 10ms
3. GET /styles.css          → 10ms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2번과 3번 결과가 먼저 준비되어도 1번 응답이 끝날 때까지 전송할 수 없습니다. 큐의 머리가 뒤 항목을 가로막는 이 현상이 HTTP/1.1 파이프라이닝의 HOL 블로킹입니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;브라우저와 중간 프록시가 파이프라이닝을 일관되게 구현하기 어려웠고, 연결 실패 뒤 어느 요청을 안전하게 다시 보낼지도 판단해야 했습니다. 실전에서는 여러 TCP 연결을 병렬로 여는 방식이 더 예측 가능했고 파이프라이닝은 주류가 되지 못했습니다.&lt;/p&gt;
&lt;h2&gt;HTTP/2와 HTTP/3는 무엇을 바꿨나&lt;/h2&gt;
&lt;p&gt;HTTP/2는 하나의 연결 안에서 요청과 응답을 프레임으로 나누고 스트림 ID로 구분합니다. 느린 스트림 하나가 있어도 다른 스트림의 응답 프레임을 먼저 보낼 수 있으므로 &lt;strong&gt;HTTP/1.1의 애플리케이션 계층 HOL 블로킹&lt;/strong&gt;을 없앴습니다.&lt;/p&gt;
&lt;p&gt;하지만 모든 스트림이 하나의 TCP 연결을 공유합니다. TCP 패킷 하나가 유실되면 그 패킷이 재전송될 때까지 연결 위의 스트림들이 함께 기다릴 수 있습니다. 이것은 &lt;strong&gt;전송 계층의 HOL 블로킹&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;HTTP/3는 TCP 대신 QUIC을 사용하고 스트림별 전송 상태를 분리해, 한 스트림의 패킷 손실이 다른 스트림까지 막는 문제를 줄였습니다.&lt;/p&gt;
&lt;p&gt;정리하면 파이프라이닝의 아이디어 자체—한 연결을 놀리지 않고 여러 작업을 겹치는 것—는 맞았습니다. 실패한 부분은 응답을 순서대로 내보내야 하는 HTTP/1.1의 메시지 구조였고, 이후 버전은 스트림 식별과 다중화로 그 제약을 제거했습니다.&lt;/p&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9112.html&quot;&gt;RFC 9112: HTTP/1.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9113.html&quot;&gt;RFC 9113: HTTP/2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/rfc/rfc9114.html&quot;&gt;RFC 9114: HTTP/3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>http</category><category>pipelining</category><category>keep-alive</category><category>tcp</category><category>rtt</category><category>HOL</category><author>Jinmu Go</author></item><item><title>모노레포 Storybook Composition 설정하기</title><link>https://jinmugo.com/posts/monorepo-storybook-composition/</link><guid isPermaLink="true">https://jinmugo.com/posts/monorepo-storybook-composition/</guid><description>Next.js 앱과 UI 패키지의 Storybook을 독립적으로 운영하면서 refs, wait-on, Chromatic permalink로 하나의 Composition 허브에 모으는 방법.</description><pubDate>Tue, 13 May 2025 13:02:43 GMT</pubDate><content:encoded>&lt;p&gt;모노레포에서 Next.js 앱과 UI 패키지의 Storybook을 분리하고도 한곳에서 보고 싶다면, &lt;strong&gt;각 Storybook은 독립적으로 실행하고 중앙 Storybook의 &lt;code&gt;refs&lt;/code&gt;로 연결&lt;/strong&gt;하면 됩니다. 이것이 Storybook Composition입니다.&lt;/p&gt;
&lt;p&gt;다만 Composition이 항상 필요한 것은 아닙니다. 현재 Storybook은 Next.js 전용 프레임워크를 제공하므로, 의존성과 배포 주기가 같은 작은 프로젝트라면 Next.js Storybook 하나에서 UI 패키지 스토리까지 읽는 편이 더 단순합니다. 저는 UI 패키지와 웹 앱의 설정·소유권·배포를 분리할 필요가 있어 Composition을 선택했습니다.&lt;/p&gt;
&lt;h2&gt;사용한 구조&lt;/h2&gt;
&lt;p&gt;처음에는 &lt;code&gt;packages/ui&lt;/code&gt;의 컴포넌트만 공통 Storybook에서 보여주고 있었습니다. 서비스 페이지의 조합과 Provider 상태까지 확인하려고 &lt;code&gt;apps/web&lt;/code&gt; 코드를 그대로 가져오자, Next.js 환경과 웹 앱의 의존성이 공통 Storybook으로 번졌습니다.&lt;/p&gt;
&lt;p&gt;그래서 다음처럼 역할을 나눴습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;monorepo/
├── apps/
│   ├── storybook/          # Composition 호스트, :6006
│   └── web/
│       └── .storybook/     # Next.js 앱 Storybook, :6008
└── packages/
    └── ui/
        └── .storybook/     # UI 패키지 Storybook, :6007
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;packages/ui&lt;/code&gt;: 재사용 컴포넌트와 디자인 시스템 스토리&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apps/web&lt;/code&gt;: 실제 페이지, 레이아웃, 앱 Provider에 의존하는 스토리&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apps/storybook&lt;/code&gt;: 컴포넌트 코드를 가져오지 않고 두 Storybook을 탐색하는 허브&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next.js 앱에는 현재 권장되는 &lt;code&gt;@storybook/nextjs-vite&lt;/code&gt;를 사용합니다. Next.js 14.1 이상과 Vite 5 이상이 필요하며, &lt;code&gt;next/image&lt;/code&gt;, 라우팅, &lt;code&gt;next/head&lt;/code&gt;, 폰트 같은 기능을 지원합니다. Composition을 택한 이유는 Next.js를 Storybook에서 실행할 수 없어서가 아니라 &lt;strong&gt;각 영역의 의존성과 설정을 격리하기 위해서&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h2&gt;로컬 Composition 설정&lt;/h2&gt;
&lt;p&gt;호스트인 &lt;code&gt;apps/storybook/.storybook/main.ts&lt;/code&gt;에 &lt;code&gt;refs&lt;/code&gt;를 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import type { StorybookConfig } from &quot;@storybook/react-vite&quot;

const config: StorybookConfig = {
  stories: [&quot;../src/README.mdx&quot;],
  framework: &quot;@storybook/react-vite&quot;,
  refs: (config, { configType }) =&amp;gt; {
    if (configType === &quot;DEVELOPMENT&quot;) {
      return {
        ui: {
          title: &quot;UI Development&quot;,
          url: &quot;http://localhost:6007&quot;,
        },
        web: {
          title: &quot;Web Development&quot;,
          url: &quot;http://localhost:6008&quot;,
        },
      }
    }

    return {
      ui: {
        title: &quot;UI&quot;,
        url: &quot;https://main--&amp;lt;ui-app-id&amp;gt;.chromatic.com&quot;,
      },
      web: {
        title: &quot;Web&quot;,
        url: &quot;https://main--&amp;lt;web-app-id&amp;gt;.chromatic.com&quot;,
      },
    }
  },
}

export default config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;포트 매핑은 처음부터 끝까지 같아야 합니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;th&gt;주소&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Composition 호스트&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://localhost:6006&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI 패키지&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://localhost:6007&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js 웹 앱&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://localhost:6008&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Storybook은 참조 URL의 &lt;code&gt;index.json&lt;/code&gt;을 읽어 스토리 목록을 탐색 패널에 표시합니다. 따라서 &lt;code&gt;http://localhost:6007/index.json&lt;/code&gt;과 &lt;code&gt;http://localhost:6008/index.json&lt;/code&gt;이 브라우저에서 열려야 합니다. 참조 Storybook의 프레임도 사용자의 브라우저가 직접 불러오므로, 사내 주소나 인증이 필요한 주소라면 열람자도 접근 권한이 있어야 합니다.&lt;/p&gt;
&lt;h2&gt;세 Storybook을 순서에 안전하게 실행하기&lt;/h2&gt;
&lt;p&gt;각 Storybook을 별도 터미널에서 켜도 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 터미널 1
pnpm --filter ./packages/ui storybook -- --port 6007

# 터미널 2
pnpm --filter ./apps/web storybook -- --port 6008

# 터미널 3
pnpm --filter ./apps/storybook storybook -- --port 6006
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turborepo처럼 세 작업을 병렬 시작하면 호스트가 먼저 뜰 수 있습니다. 이때 참조 서버가 아직 준비되지 않아 &lt;code&gt;index.json&lt;/code&gt; 요청이 실패합니다. 호스트 패키지의 스크립트에서 이미 사용 중인 &lt;code&gt;wait-on&lt;/code&gt;으로 두 메타데이터 URL이 2xx를 반환할 때까지만 기다리면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;storybook&quot;: &quot;wait-on http-get://localhost:6007/index.json http-get://localhost:6008/index.json &amp;amp;&amp;amp; storybook dev -p 6006&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;http-get://&lt;/code&gt;를 쓴 이유는 포트가 열렸는지만 보는 것이 아니라 실제 &lt;code&gt;index.json&lt;/code&gt;에 GET 요청을 보내 성공 응답을 확인하기 위해서입니다.&lt;/p&gt;
&lt;h3&gt;‘CORS 오류’와 준비 실패를 구분하기&lt;/h3&gt;
&lt;p&gt;제가 처음 본 콘솔 메시지는 다음과 같았습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wait-on&lt;/code&gt;이 해결하는 것은 &lt;strong&gt;시작 순서 경쟁&lt;/strong&gt;뿐입니다. 참조 서버가 완전히 뜬 뒤에도 브라우저가 &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt;이나 credentials 오류를 표시한다면 이는 실제 CORS 설정 문제이므로, 참조 Storybook을 제공하는 프록시·호스팅의 응답 헤더나 동일 출처 구성을 고쳐야 합니다. 대기 시간을 늘려서는 해결되지 않습니다.&lt;/p&gt;
&lt;p&gt;확인은 두 단계면 충분합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;참조 Storybook의 &lt;code&gt;/index.json&lt;/code&gt;이 2xx와 JSON을 반환하는지 확인&lt;/li&gt;
&lt;li&gt;호스트 Storybook을 열어 UI와 Web 항목이 탐색 패널에 표시되는지 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Chromatic 배포 주소 연결하기&lt;/h2&gt;
&lt;p&gt;배포 환경에서도 UI, Web, Composition을 각각 빌드합니다. 호스트의 &lt;code&gt;refs&lt;/code&gt;에는 매 빌드마다 달라지는 주소 대신 Chromatic의 브랜치 permalink를 넣었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://&amp;lt;branch&amp;gt;--&amp;lt;appid&amp;gt;.chromatic.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;예를 들어 &lt;code&gt;main&lt;/code&gt; 브랜치라면 &lt;code&gt;https://main--&amp;lt;appid&amp;gt;.chromatic.com&lt;/code&gt; 형태입니다. 이 주소는 해당 브랜치의 최신 빌드를 가리키므로 호스트 설정을 매번 바꿀 필요가 없습니다.&lt;/p&gt;
&lt;p&gt;공개 범위도 함께 확인해야 합니다. Composition 호스트가 공개되어 있어도 참조한 Chromatic 프로젝트가 비공개라면 권한 없는 사용자는 스토리를 볼 수 없습니다.&lt;/p&gt;
&lt;h2&gt;언제 Composition을 선택할까&lt;/h2&gt;
&lt;p&gt;다음 조건이면 Composition의 분리 비용이 의미가 있었습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UI 패키지와 앱의 Storybook 프레임워크 또는 설정이 다르다.&lt;/li&gt;
&lt;li&gt;각 영역을 다른 팀이 소유하거나 독립적으로 배포한다.&lt;/li&gt;
&lt;li&gt;호스트가 참조 패키지의 런타임 의존성을 가져서는 안 된다.&lt;/li&gt;
&lt;li&gt;여러 저장소나 기술 스택의 Storybook을 한 탐색 패널에서 봐야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반대로 같은 팀이 같은 주기로 배포하고 스토리 수가 많지 않다면 단일 Storybook이 더 낫습니다. Composition은 코드를 합치는 기능이 아니라 &lt;strong&gt;이미 실행 가능한 Storybook들을 URL로 묶는 기능&lt;/strong&gt;이므로, 참조 대상의 빌드·호스팅·접근 권한은 여전히 각각 관리해야 합니다.&lt;/p&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://storybook.js.org/docs/sharing/storybook-composition&quot;&gt;Storybook Composition 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://storybook.js.org/docs/get-started/frameworks/nextjs-vite/?renderer=react&quot;&gt;Storybook for Next.js with Vite 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chromatic.com/docs/permalinks/&quot;&gt;Chromatic permalink 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jeffbski/wait-on&quot;&gt;wait-on 저장소&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://turborepo.com/docs/guides/tools/storybook&quot;&gt;Turborepo의 Storybook 가이드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>storybook-composition</category><category>monorepo</category><category>storybook</category><category>nextjs</category><category>chromatic</category><author>Jinmu Go</author></item><item><title>프록시, 이제는 더이상 물러날 곳이 없다!</title><link>https://jinmugo.com/posts/proxy-no-more-retreat/</link><guid isPermaLink="true">https://jinmugo.com/posts/proxy-no-more-retreat/</guid><description>프록시의 기본 역할부터 포워드·리버스 프록시의 위치, 트래픽을 전달받는 방식, 라우팅 활용과 단점까지 한 흐름으로 정리합니다.</description><pubDate>Sat, 12 Jul 2025 06:31:40 GMT</pubDate><content:encoded>&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;지난 7월 12일에 진행된 &lt;a href=&quot;https://ausg.me/&quot;&gt;Ausg&lt;/a&gt; 9기 면접에서 Reverse Proxy에 대해 제대로 답변하지 못하여. 그걸 기념(?)하기 위해 미루고 미루던 Proxy에 대해 이번 기회를 통해 블로그 글을 써서 정리해 보려 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
이 글은 다양한 자료를 참고했지만, 특히 &lt;a href=&quot;https://dl.ebooksworld.ir/books/HTTP.The.Definitive.Guide.Brian.Totty.David.Gourley.OReilly.9781565925090.EBooksWorld.ir.pdf&quot;&gt;Http definitive guide&lt;/a&gt;의 Proxies 챕터를 기반으로 작성되었습니다. 또한 여기서 다루는 프록시는 HTTP/HTTPS Protocol을 사용하는 Web Proxy로 한정합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Proxy&lt;/h2&gt;
&lt;p&gt;프록시(Proxy)는 단어 뜻 그대로 &lt;strong&gt;&apos;대리인&apos;&lt;/strong&gt; 또는 &lt;strong&gt;&apos;중개자&apos;&lt;/strong&gt; 를 의미합니다. 웹의 세계에서 프록시는 클라이언트와 서버라는 두 주체 &lt;strong&gt;사이&lt;/strong&gt;에서, 둘 사이의 HTTP 통신을 대리하는 중개인입니다. 클라이언트와 서버 사이에 있다는 특징 때문에 Proxy는 클라이언트로부터 요청을 받고 응답을 반환하는 &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;h2&gt;Why Use Proxy?&lt;/h2&gt;
&lt;p&gt;앞서 프록시를 &apos;만능 관제탑&apos;에 비유했습니다. 그렇다면 이 관제탑은 왜 필요한 걸까요? 단순히 메시지를 전달하는 것 이상의 어떤 가치를 제공하기에, 우리는 기꺼이 TCP/IP 아키텍처의 복잡성을 감수하고 프록시를 도입하는 걸까요?&lt;/p&gt;
&lt;p&gt;중요한 점은, 지금부터 설명할 대부분의 기능은 트래픽의 방향에 따라 포워드 프록시와 리버스 프록시 양쪽에서 모두 적용된다는 것입니다.&lt;/p&gt;
&lt;h3&gt;보안 강화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;방화벽(Firewall): 프록시는 네트워크의 경계에서 외부로 나가거나 내부로 들어오는 트래픽을 관리하는 방화벽 역할을 합니다. 특정 종류의 악성 요청을 차단하고 내부 네트워크를 보호하는 &lt;strong&gt;단일 방어 지점&lt;/strong&gt;이 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;접근 제어 및 필터링: 학교나 회사에서 특정 웹사이트 접근을 막는 것이 바로 프록시의 역할입니다. 부적절한 콘텐츠를 필터링하고, 특정 리소스에 대한 접근 권한을 중앙에서 관리하며 모든 접근 기록을 남길 수 있습니다.&lt;/li&gt;
&lt;li&gt;익명화(Anonymization): 프록시는 사용자의 요청을 대신해서 서버에 요청을 보내기 때문에 사용자의 IP주소, 브라우저 정보, 쿠키 등 개인을 식별할 수 있는 정보를 HTTP Header또는 Body에서 제거하여 개인정보 보호와 익명성을 강화할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;성능 최적화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;웹 캐싱(Web Caching): 프록시의 강력한 기능 중 하나입니다. 자주 요청되는 콘텐츠(ex: logo, CSS File등)의 복사본을 프록시 서버에 저장해두고. 이후 동일한 요청이 들어오면 멀리 있는 원본 서버까지 가지 않고 캐시된 데이터를 즉시 전달하여 속도를 높이고 서버까지 가는 트래픽을 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;부하 분산 (Load Balancing): 대규모 서비스에서는 여러 대의 웹 서버가 동시에 운영됩니다. 프록시는 들어오는 요청을 여러 서버에 골고루 분산하여 특정 서버에 과부하가 걸리는 것을 막고 전체 시스템의 안정성과 효율성을 높입니다.&lt;/li&gt;
&lt;li&gt;서버 가속(Reverse Proxy): 웹서버 바로 앞에 위치하여 스스로를 웹 서버처럼 위장하는 프록시를 &lt;strong&gt;리버스 프록시(Reverse Proxy)&lt;/strong&gt; 또는 &lt;strong&gt;서로게이트(Surrogate)&lt;/strong&gt; 라고 합니다. SSL 암호화 처리, 정적 콘텐츠 캐싱등 무거운 작업을 대신 처리하여 실제 웹 서버가 핵심적인 비즈니스 로직 처리에만 집중할 수 있도록 만들어 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;콘텐츠 변환 및 라우팅&lt;/h2&gt;
&lt;p&gt;프록시는 단순 전달을 넘어, 콘텐츠를 &lt;strong&gt;가공(Transcoding)&lt;/strong&gt; 하거나 요청에 따라 목적지를 바꾸는 &lt;strong&gt;라우팅&lt;/strong&gt;도 가능합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;콘텐츠 변환(Transcoding): 프록시는 콘텐츠를 클라이언트에게 전달하기 전에 그 형식을 바꿀 수 있습니다. 예를 들어, 고화질 이미지를 모바일 기기에 맞게 저용량 JPEG 파일로 변환하거나, 복잡한 웹페이지를 단순한 텍스트로 변환하여 전송할 수 있습니다.&lt;/li&gt;
&lt;li&gt;콘텐츠 라우팅: 요청의 내용에 따라 트래픽의 목적지를 바꿀 수 있습니다. &apos;/api&apos;로 시작하는 요청은 애플리케이션 서버로, &apos;/video&apos; 요청은 미디어 스트리밍 서버로 보내는 식의 제어가 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Where Do Proxies Go?&lt;/h2&gt;
&lt;p&gt;프록시는 네트워크상 다양한 위치에 배포되어 각기 다른 목적을 수행합니다. 프록시의 위치와 트래픽을 유도하는 방식에 따라 그 역할과 기능이 결정됩니다. 다시말해, 프록시의 위치가 곧 프록시의 역할과 기능을 정의하는 셈입니다.&lt;/p&gt;
&lt;h3&gt;프록시 서버의 다양한 배포 위치&lt;/h3&gt;
&lt;p&gt;프록시는 트래픽의 흐름을 제어하기 위해 전략적으로 배치됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Egress Proxy: 로컬 네트워크의 &lt;strong&gt;출구&lt;/strong&gt;에 위치합니다. 주로 기업이나 기관에서 내부 사용자들이 인터넷으로 보내는 트래픽을 제어할 목적으로 사용됩니다. 인터넷 대역폭 비용을 절감하고, 직원들이 유해 사이트에 접속하는 것을 막는 콘텐츠 필터링, 바이러스 차단 등 보안 강화 목적으로 배치됩니다.&lt;/li&gt;
&lt;li&gt;Access / Ingress Proxy: ISP(Internet Service Provider)의 &lt;strong&gt;접속 지점&lt;/strong&gt;에 위치하여, 수 많은 고객들로부터 오는 요청을 종합적으로 처리합니다. 주된 목적은 인기 있는 콘텐츠를 캐싱하여 사용자들의 다운로드 속도를 개선하고, 인터넷 대역폭 비용을 절감하는 것입니다.&lt;/li&gt;
&lt;li&gt;Surrogate / Reverse Proxy: 웹 서버 바로 &lt;strong&gt;앞&lt;/strong&gt;, 즉 &lt;strong&gt;네트워크의 끝단&lt;/strong&gt;에 위치합니다. 웹 서버인 것처럼 동작하여 모든 요청을 먼저 받습니다. 이를 통해 DDos 공격 방어 또는 자주 요청되는 콘텐츠를 캐싱하여 실제 웹 서버의 부하를 줄여 성능을 크게 향상시킵니다. CDN(Content Delivery Network)이 리버스 프록시의 대표적인 예시입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;프록시 계층 구조&lt;/h3&gt;
&lt;p&gt;프록시는 단독으로 쓰이기도 하지만, 여러 프록시가 사슬처럼 연결된 &lt;strong&gt;계층 구조(Hierarchy)&lt;/strong&gt; 를 이루기도 합니다. 요청 메시지(HTTP Requeset)는 최종 목적지인 원본 서버에 도달할 때까지 여러 프록시를 순차적으로 거쳐갈 수 있습니다.
이때, 요청이 전달되는 경로에서 &lt;strong&gt;다음 프록시를 &apos;부모&apos;&lt;/strong&gt;, &lt;strong&gt;이전 프록시를 &apos;자식&apos;&lt;/strong&gt; 이라고 부릅니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!note] Dynamic Routing
프록시는 단순히 정해진 부모에게만 요청을 보내는 것이 아니라, 다양한 조건에 따라 요청을 동적으로 다른 프록시나 서버로 전달할 수 있습니다.
아래는 몇가지 Dynamic Routing의 예시입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Load balancing: 자식 프록시는 부모 프록시의 현재 작업량 수준에 따라 부모 프록시를 선택하여 부하를 분산시킬 수 있습니다. Load balancing의 알고리즘은 여러가지가 있으니 궁금하시다면 찾아보시는 것도 좋을 것 같습니다.&lt;/li&gt;
&lt;li&gt;Geographic proximity routing: 자식 프록시는 원본 서버의 지리적 지역을 담당하는 상위 프록시를 선택할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Protocol/type routing: 자식 프록시는 URI에 따라 다른 부모 프록시 및 원본 서버로 라우팅 할 수 있습니다. 특정 유형의 URI는 특수 프로토콜 처리를 위해 요청이 특수 프록시 서버를 통해 전송될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;How Proxies Get Traffic&lt;/h2&gt;
&lt;p&gt;웹 브라우저(ex: safari, google chrome, arc)는 일반적으로 웹 서버와 직접 통신하기 때문에 HTTP 트래픽이 프록시를 거쳐가는 방법을 먼저 설정해야 합니다. 아무리 강력한 프록시라도, 트래픽이 프록시를 거치지 않고 그냥 지나쳐간다면 아무 소용이 없습니다. 프락시가 제 역할을 하려면 클라이언트의 웹 트래픽이 프록시를 거치도록 만들어야 합니다. 이 트래픽을 유도하는 방식은 크게 서버 측에서 제어하는 방식과 클라이언트/네트워크에서 제어하는 방식으로 나눌 수 있습니다.&lt;/p&gt;
&lt;p&gt;이번 글에서는 특히 &lt;strong&gt;서버 측 제어 방식을 더 자세히 살펴보겠습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;DNS 수정&lt;/h3&gt;
&lt;p&gt;리버시 프록시(또는 서로게이트)가 트래픽을 받는 가장 일반적인 방법입니다. &lt;strong&gt;핵심 원리는 리버스 프록시가 실제 웹 서버의 신원(identity)을 이어받는 것&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;p&gt;작동 방식은 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;DNS 레코드 설정: 관리자는 웹사이트의 호스트 명(예: jinmu.me)에 대한 DNS 레코드를 설정할 때, 실제 웹 서버의 IP 주소가 아닌 리버스 프록시의 IP 주소를 등록합니다.&lt;/li&gt;
&lt;li&gt;클라이언트의 DNS 조회: 사용자가 브라우저에 jinmu.me을 입력하면, 클라이언트의 컴퓨터는 DNS 서버에 이 도메인의 IP 주소를 물어봅니다.&lt;/li&gt;
&lt;li&gt;프록시 IP 주소 반환: DNS 서버는 설정된 대로 리버스 프록시의 IP 주소를 응답으로 돌려줍니다.&lt;/li&gt;
&lt;li&gt;프록시로 요청 전송: &lt;strong&gt;클라이언트는 이 IP 주소가 프록시의 것이라는 사실을 전혀 알지 못한 채&lt;/strong&gt;, 해당 주소로 HTTP 요청을 보냅니다. &lt;strong&gt;클라이언트로서는 프록시가 바로 jinmu.me 웹 서버 그 자체인 셈&lt;/strong&gt;입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;만약 DNS의 동작에 대해 알고 싶으시다면 제가 작성한 &lt;a href=&quot;/series&quot;&gt;DNS Series&lt;/a&gt;를 보시는 것도 추천해 드립니다!&lt;/p&gt;
&lt;p&gt;이 방식 덕분에, 클라이언트는 아무것도 몰라도 되고 서버 아키텍처는 자유롭게 변경될 수 있습니다.&lt;/p&gt;
&lt;h3&gt;HTTP Redirection&lt;/h3&gt;
&lt;p&gt;웹 서버가 직접 클라이언트에게 프록시를 사용하라고 &apos;지시&apos;하는 방법도 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트가 웹 서버에 리소스를 요청합니다.&lt;/li&gt;
&lt;li&gt;웹 서버는 요청을 바로 처리하는 대신, &lt;strong&gt;305 Use Proxy&lt;/strong&gt;라는 특별한 HTTP 상태 코드와 함께 응답을 보냅니다.&lt;/li&gt;
&lt;li&gt;이 응답의 Location 헤더에는 클라이언트가 사용해야 할 프록시 서버의 주소가 포함되어 있습니다.&lt;/li&gt;
&lt;li&gt;이 응답을 받은 클라이언트는 동일한 요청을 Location 헤더에 명시된 프록시 서버를 통해 다시 보내야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;하지만 이 &lt;code&gt;305&lt;/code&gt; 상태 코드는 &lt;strong&gt;현재는 거의 사용되지 않으며, 많은 최신 브라우저에서 보안상의 이유로 지원이 중단되었습니다.&lt;/strong&gt; 악의적인 서버가 사용자의 트래픽을 감시할 수 있는 프록시로 유도할 수 있는 잠재적 위험 때문입니다. 따라서 개념적으로는 존재하지만, 실제 환경에서는 보기 드문 방식입니다.&lt;/p&gt;
&lt;h3&gt;클라이언트 및 네트워크 측 제어 방식&lt;/h3&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;: 가장 고전적인 방법입니다. 웹 브라우저 설정에 들어가 &quot;이 프록시 서버를 경유해서 인터넷에 접속해라&quot;라고 &lt;strong&gt;수동으로 지정&lt;/strong&gt;하는 것입니다. 또는 특정 규칙이 담긴 스크립트(PAC 파일)를 사용하여 URL에 따라 프록시 사용 여부를 동적으로 결정할 수도 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;네트워크 수정 (투명 프록시)&lt;/strong&gt;: 클라이언트나 사용자는 전혀 모르는 사이에, 길목에 있는 &lt;strong&gt;네트워크 장비(라우터, 스위치)가 보이지 않는 그물을 쳐서&lt;/strong&gt; HTTP 트래픽을 강제로 프록시 서버로 보내는 방식입니다. 사용자에게는 보이지 않는다고 하여 &lt;strong&gt;&apos;투명(Transparent) 프록시&apos;&lt;/strong&gt; 라고도 불립니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;단점&lt;/h2&gt;
&lt;p&gt;이렇게 강력한 기능을 제공하는 프록시지만, 모든 기술이 그렇듯 빛이 있으면 그림자도 있는 법입니다. &apos;모든 것을 통제한다&apos;라는 말은 &apos;모든 것이 그에게 의존한다&apos;라는 뜻이기도 하니까요. 프록시를 아키텍처에 도입할 때 반드시 고려해야 할 몇 가지 단점들이 존재합니다.&lt;/p&gt;
&lt;h3&gt;단일 장애 지점 (Single Point of Failure, SPOF)&lt;/h3&gt;
&lt;p&gt;프록시의 가장 치명적인 약점입니다. 모든 트래픽이 프록시를 거쳐야 한다는 것은, 만약 프록시 서버가 다운된다면 전체 서비스가 마비되는 결과를 초래할 수 있다는 의미입니다. 클라이언트와 서버 사이의 유일한 통로가 막혀버리는 셈이죠. 이 때문에 실제 운영 환경에서는 프록시 서버를 여러 대 두어 이중화(Redundancy)하거나, 클라우드 환경의 로드 밸런서처럼 자체적으로 고가용성을 보장하는 서비스를 사용하는 것이 사실상 필수적입니다.&lt;/p&gt;
&lt;h3&gt;성능 병목 및 복잡성&lt;/h3&gt;
&lt;p&gt;프록시는 요청을 중간에서 처리하고 전달하는 과정에서 아주 약간의 지연(Latency)을 추가합니다. 대부분의 경우 이는 무시할 수 있는 수준이지만, 만약 프록시 서버의 사양이 부족하거나 설정이 최적화되지 않았다면 오히려 전체 시스템의 성능을 저하시키는 병목이 될 수 있습니다. 또한, 프록시 계층 구조나 복잡한 라우팅 규칙은 아키텍처의 복잡도를 높여 설정하고 관리하는 것을 굉장히 까다롭게 만듭니다.&lt;/p&gt;
&lt;h3&gt;보안 및 개인정보 문제&lt;/h3&gt;
&lt;p&gt;보안 강화를 위해 도입한 프록시가 역설적으로 보안의 허점이 될 수도 있습니다. 모든 트래픽이 집중되는 만큼, 프록시 서버 자체가 해킹당하면 시스템 전체가 위험에 노출됩니다. 특히 SSL/TLS 트래픽을 복호화(SSL Termination)하여 내용을 검사하는 경우, 프록시는 암호화되지 않은 민감한 데이터를 모두 들여다볼 수 있게 됩니다. 이는 관리자에게 큰 책임감을 요구하며, 프록시 서버 자체의 보안을 철저히 관리해야 하는 또 다른 숙제를 안겨줍니다.&lt;/p&gt;
&lt;h2&gt;Outro&lt;/h2&gt;
&lt;p&gt;이렇게 프록시의 정의부터 시작해, 사용하는 이유(Why), 다양한 위치(Where), 그리고 트래픽을 제어하는 방식(How) 그리고 마지막으로 단점까지 정리해 봤습니다.
AUSG 면접에서 리버스 프록시에 대해 제대로 답변하지 못했던 아쉬움으로 시작된 포스팅이었지만. 단순히 &apos;리버스 프록시는 웹 서버 앞에 두는 것&apos;이라는 표면적인 이해를 넘어, 왜 그것이 거기에 있어야만 하는지, 어떤 원리로 클라이언트가 자신도 모르게 프록시와 통신하게 되는지를 알 수 있었던 시간이었습니다. 글을 쓰기 위해 자료를 찾고 문장을 다듬는 과정이야말로 가장 확실한 공부 방법이라는 것을 다시 한번 깨닫게 되네요.&lt;/p&gt;
&lt;p&gt;이 글이 과거의 저처럼 프록시에 대해 막연하게 알고 있었던 분들에게 도움이 되길 바랍니다. 감사합니다.&lt;/p&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://dl.ebooksworld.ir/books/HTTP.The.Definitive.Guide.Brian.Totty.David.Gourley.OReilly.9781565925090.EBooksWorld.ir.pdf&quot;&gt;Http definitive guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;[10분 테코톡] 🐿 제이미의 Forward Proxy, Reverse Proxy, Load Balancer&quot;
image: &quot;https://i.ytimg.com/vi/YxwYhenZ3BE/maxresdefault.jpg&quot;
description: &quot;🙋‍♀️ 우아한테크코스의 크루들이 진행하는 10분 테크토크입니다. 🙋‍♂️’10분 테코톡’이란 우아한테크코스 과정을 진행하며 크루(수강생)들이 동료들과 학습한 내용을 공유하고 이야기하는 시간입니다. 서로가 성장하기 위해 지식을 나누고 대화하며 생각해보는 시간으로 자기 주도적인…&quot;
url: &quot;https://www.youtube.com/watch?v=YxwYhenZ3BE&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;프록시를 알려드림&quot;
image: &quot;https://i.ytimg.com/vi/Je3PMpvwjtE/maxresdefault.jpg&quot;
description: &quot;#개발자 #Proxy #몰컴⭐️ 코드팩토리 통합링크 (모든 강의 및 서적) ⭐️https://links.codefactory.ai👉 NestJS 마스터클래스https://bit.ly/fcnestjs👉 플터터 초급https://inf.run/Sjuw👉 플터터 중급https://…&quot;
url: &quot;https://www.youtube.com/watch?v=Je3PMpvwjtE&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><category>ReverseProxy</category><category>ForwardProxy</category><category>Proxy</category><category>Network</category><author>Jinmu Go</author></item><item><title>달팽이 &apos;나름 전력질주중&apos;</title><link>https://jinmugo.com/posts/snail-sprinting-in-its-own-way/</link><guid isPermaLink="true">https://jinmugo.com/posts/snail-sprinting-in-its-own-way/</guid><description>느리더라도 나름대로 전력질주하는 달팽이처럼, 자신의 속도로 하루를 살아가는 사람들에게 건네는 짧은 응원.</description><pubDate>Wed, 04 Jun 2025 02:33:54 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;title: &quot;힘내&quot;
image: &quot;https://i.ytimg.com/vi/ufULhdEWlxE/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AHOBYAC0AWKAgwIABABGH8gKygrMA8=&amp;amp;rs=AOn4CLDs7_DLq83CwCqCHVVo1caN9OkCUw&quot;
description: &quot;#playlist #플리 #밴드 00:01 챠우챠우04:23 아름다운 것09:12 나는13:24 너의 한마디17:23 우린 노래가 될까22:12 사랑한다는 말로도 위로가 되지 않는27:00 당신의 밤은 나의 밤과 같습니까30:45 동거35:10 라일락38:40 사랑해&quot;
url: &quot;https://www.youtube.com/watch?v=ufULhdEWlxE&amp;amp;t=2327s&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;최근 자주 듣고 있는 플레이리스트다. 괜히 힘들 때 틀어서 보고있노라면 멋진 사람이 된 것같은 기분이 든다.
특히 처음 나오는 마츠오카 슈조선수의 말과 델리스파이스 챠우챠우의 페어링이 기분 좋다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!quote]
그냥 하루하루를 살고 있나?
가야 될 길이 어딘지를 모르겠어?
&quot;살아있음을 느끼고 싶어!&quot;
간단해
과거는 생각할 필요 없어
&quot;내가 왜 그딴 짓을!&quot;
그건 그냥 분노만 쌓일 뿐이야
미래도 생각할 필요 없어
&quot;진짜 괜찮을까?&quot;
걱정만 불러 일으킬 뿐이야
그렇다면?
그냥 해!
지금 이 순간에 네 인생을 거는거야!
좋아!
모두가 지금 자신의 인생을 건다면
모두...
살아있단거야!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;그다음 플레이리스트에서 아래 사진이 뒤따라 나온다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;달팽이 달리기 대회에서 자신의 달팽이에게 독려하는 말인데, 괜히 위안을 준다. 나 역시도 마찬가지다. 뉴스에 3초도 남짓도 나오지 않은 만나보지도 못한 영국의 한 달팽이 주인에게 위로를 받을 수 있다니.
참 좋은 세상이다.&lt;/p&gt;
&lt;p&gt;달팽이 같은 사람들. 자신이 다른 사람들보다 느리다고 생각하는 모든 달팽이에게 바친다.&lt;/p&gt;
</content:encoded><category>달팽이</category><author>Jinmu Go</author></item><item><title>ssh-copy-id를 이용해서 원격지 접속쉽게 설정하기</title><link>https://jinmugo.com/posts/ssh-copy-id/</link><guid isPermaLink="true">https://jinmugo.com/posts/ssh-copy-id/</guid><description>SSH 공개키 인증의 동작 원리와 ssh-copy-id로 공개키를 원격 서버에 등록하는 방법, 필요한 sshd 설정을 설명합니다.</description><pubDate>Tue, 02 Sep 2025 08:17:04 GMT</pubDate><content:encoded>&lt;p&gt;원격 서버에 SSH로 접속할 때마다 비밀번호 입력하는게 너무 귀찮다. ssh는 GSSAPI, host-based등등 다양한 인증방식을 지원하지만 나는 주로 &lt;strong&gt;비밀번호(대칭키 암호화)&lt;/strong&gt; 또는 &lt;strong&gt;공개키 방식 인증&lt;/strong&gt;을 사용한다.
(ssh의 인증방식에 대해 궁금하다면 &lt;code&gt;man ssh&lt;/code&gt;의 &lt;code&gt;AUTHENTICATION&lt;/code&gt; Section을 확인)&lt;/p&gt;
&lt;p&gt;요즘은 비밀번호 치는 것도 번거로워서 내가 갖고 있는 모든 서버의 ssh접속 인증을 모두 공개키 방식으로 바꾸는 중이다. 이때 쓸수 있는게 &lt;code&gt;ssh-copy-id&lt;/code&gt;명령인데 오늘은 이 친구에 대해 알아볼 예정이다.&lt;/p&gt;
&lt;p&gt;그 전에 공개키 암호 방식에 대해 간략하게 알아보자.&lt;/p&gt;
&lt;h2&gt;공개키 암호 방식&lt;/h2&gt;
&lt;p&gt;개인키(Private Key)와 공개키(Public Key)를 이용한 인증방식이다. 비밀번호와 달리 암호화와 복호화에 이용하는 키가 다른 방식이다.&lt;/p&gt;
&lt;p&gt;공개키 방식은 출제하는 측과 문제를 푸는 측이 존재한다. 출제자는 &lt;strong&gt;개인키&lt;/strong&gt; 없이는 풀기 어려운 수학적 문제를 &lt;strong&gt;공개키&lt;/strong&gt;를 기반으로 만든다.
문제를 푸는 측은 개인키가 없다면 이 문제를 풀기 어려우며 개인키를 가진 사람만이 문제를 풀어 인증을 할 수 있다. 심지어 공개키를 갖고 있는 사람이 문제를 만들어도 개인키를 갖고 있지 않다면 문제를 풀 수 없다. 이런 특징 때문에 비대칭 암호라고 부르기도 한다.&lt;/p&gt;
&lt;p&gt;이런 특징을 이용해 ssh에서도 공개키 인증 방식을 이용한다.&lt;/p&gt;
&lt;h2&gt;ssh에서 공개키 암호&lt;/h2&gt;
&lt;p&gt;인증 과정은 총 4단계이다.&lt;/p&gt;
&lt;p&gt;준비 -&amp;gt; 접속 요청 및 정보 교환 -&amp;gt; 디지털 서명 생성 (클라이언트) -&amp;gt; 서명 검증 (서버)&lt;/p&gt;
&lt;h3&gt;준비&lt;/h3&gt;
&lt;p&gt;준비 과정에서는 client가 &lt;code&gt;ssh-keygen&lt;/code&gt;명령어로 개인키와 공개키를 만든다. 이때 &lt;code&gt;-t&lt;/code&gt;옵션으로 사용하고 싶은 key의 type을 지정할 수 있다.&lt;/p&gt;
&lt;p&gt;그리고 client는 접속하려는 원격 서버에 자신의 공개키를 미리 복사해서 &lt;code&gt;.ssh/authorized_keys&lt;/code&gt;파일에 등록해 둔다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;이때 사용할 수 있는게 &lt;code&gt;ssh-copy-id&lt;/code&gt;이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;접속 요청 및 정보 교환&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트가 서버에 SSH접속 요청&lt;/li&gt;
&lt;li&gt;서버와 사용자는 암호화 통신을 시작, 이번 통신에만 사용될 고유한 &lt;strong&gt;세션 ID(Session ID)&lt;/strong&gt; 와 같은 임시 정보를 공유한다. 이 세션 ID는 매번 접속할 때마다 바뀌는 고유한 값이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;디지털 서명 생성 (클라이언트)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트는 방금 서버와 공유한 세션 ID를 기반으로 메시지를 만든다.&lt;/li&gt;
&lt;li&gt;그리고 개인키를 활용해 메시지에 암호화를 진행한다. 이렇게 생성된 결과물이 디지털 서명이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;서명 검증 (서버)&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;클라이언트는 생성한 디지털 서명을 서버로 전송한다.&lt;/li&gt;
&lt;li&gt;서버는 &lt;code&gt;authrized_keys&lt;/code&gt;에 있는 공개키를 가져온다.&lt;/li&gt;
&lt;li&gt;이 공개키를 이용히 클라이언트가 보낸 디지털 서명이 유효한지 검증한다.검증 과정은 다음 두 가지를 수학적으로 확인한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;이 서명이 정말 &lt;strong&gt;해당 공개키와 쌍을 이루는 개인키로 만들어졌는가?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;서명이 &lt;strong&gt;처음 공유했던 세션 ID와 일치하는가?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ssh-copy-id&lt;/h2&gt;
&lt;p&gt;위에서 잠깐 언급했듯이 &lt;a href=&quot;#junbi&quot;&gt;준비&lt;/a&gt; 과정에서 사용되는 것이 &lt;code&gt;ssh-copy-id&lt;/code&gt;이다. 공개키 인증 방식에서 원격지 서버의 &lt;code&gt;~/.ssh/authrized_keys&lt;/code&gt;에 클라이언트가 생성한 공개키(public key)를 추가하는 과정이 필요한데 이 번거로운 작업을 간편하게 수행하는 것이 이 커맨드이다.&lt;/p&gt;
&lt;p&gt;기본 사용 방식은 아래와 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-copy-id [사용자명]@[서버주소]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또는 &lt;code&gt;~/.ssh/config&lt;/code&gt;에 설정된 &lt;code&gt;Host&lt;/code&gt;의 이름을 사용해도 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-copy-id [호스트명]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래는 &lt;code&gt;ssh-copy-id&lt;/code&gt;로 원격 &lt;code&gt;proxmox&lt;/code&gt; 호스트에게 public key설정을 하는 이미지이다.
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;-i&lt;/code&gt; 옵션으로 특정 공개키 파일을 지정하여 복사할 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;실행 과정&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;명령 실행&lt;/strong&gt;:  &lt;code&gt;ssh-key-2025-07-31.key.pub&lt;/code&gt;라는 특정 공개키 파일을 &lt;code&gt;proxmox&lt;/code&gt; 서버로 복사하라는 명령을 실행.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;원격 서버 접속&lt;/strong&gt;: 공개키를 서버에 등록하기 위해, 해당 서버(&lt;code&gt;root@192.168.1.107&lt;/code&gt;)에 접속한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자동 복사 및 설정&lt;/strong&gt;: 사용자가 비밀번호를 정확히 입력하면, &lt;code&gt;ssh-copy-id&lt;/code&gt;가 알아서 공개키를 원격 서버의 &lt;code&gt;authorized_keys&lt;/code&gt; 파일에 추가하고 필요한 권한 설정까지 완료.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;성공&lt;/strong&gt;: &quot;1개의 키가 추가되었다&quot;는 메시지와 함께 모든 과정이 성공적으로 끝났음을 알려준다. 이제부터는 비밀번호 없이 지정된 키를 사용해 바로 접속할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;만약 &lt;code&gt;ssh-copy-id&lt;/code&gt;가 없다면 수동으로 넣어주어야 한다 .&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat ~/.ssh/id_rsa.pub | ssh [사용자명]@[서버주소] \
&quot;mkdir -p ~/.ssh &amp;amp;&amp;amp; \
 touch ~/.ssh/authorized_keys &amp;amp;&amp;amp; \
 chmod 700 ~/.ssh &amp;amp;&amp;amp; \
 chmod 600 ~/.ssh/authorized_keys &amp;amp;&amp;amp; \
 cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 방식으로.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ssh-copy-id&lt;/code&gt;는 이 과정을 자동으로 처리해주는 커맨드이다. 구현부를 살펴보면 쉘 스크립트로 이루어져 있다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;MACOS기준 416줄 정도 되는 쉘스크립트이니 궁금하면 &lt;code&gt;which ssh-copy-id&lt;/code&gt;로 나온 경로의 파일을 열어서 확인해보길 바란다.&lt;/p&gt;
&lt;h2&gt;서버측 sshd 데몬 설정&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ssh-copy-id&lt;/code&gt;를 사용해 공개키를 성공적으로 복사했더라도, 서버의 SSH 설정이 공개키 인증을 허용하지 않으면 접속할 수 없다.&lt;/p&gt;
&lt;p&gt;원격 서버의 &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;파일을 열어서 아래 항목을 보고 변경하자.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 공개키 인증을 허용하는지 여부 (필수)
PubkeyAuthentication yes

# 허용된 공개키 목록 파일의 위치
AuthorizedKeysFile      .ssh/authorized_keys .ssh/authorized_keys2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# Systemd를 사용하는 대부분의 최신 리눅스 (Ubuntu, CentOS 7+ 등)
sudo systemctl restart sshd

# 또는
sudo systemctl restart ssh

# init.d를 사용하는 구형 시스템
sudo service ssh restart
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;원격 서버에 접속할 때마다 비밀번호를 입력하는 번거로움은 공개키 인증 방식으로 해결할 수 있다.  하지만 서버에 공개키를 등록하는 과정은 생각보다 귀찮은 수작업을 요구한다. &lt;code&gt;mkdir&lt;/code&gt;, &lt;code&gt;touch&lt;/code&gt;, &lt;code&gt;chmod&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt; 같은 여러 명령어를 실수 없이 조합해야 하기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ssh-copy-id&lt;/code&gt;는 바로 이런 실수하기 쉬운 과정을 단 한 줄의 명령어로 끝내주는 고마운 친구다. 이 명령어 하나면 원격 서버에 접속해 &lt;code&gt;authorized_keys&lt;/code&gt; 파일을 찾아 권한을 맞추고 키를 복사하는 모든 과정이 자동으로 처리된다.
이제 &lt;code&gt;ssh-copy-id&lt;/code&gt;로 귀찮음을 덜어보자.&lt;/p&gt;
</content:encoded><category>Linux</category><category>ssh</category><category>ssh-copy-id</category><author>Jinmu Go</author></item><item><title>제4회 선샤인런 하프마라톤 완주 후기</title><link>https://jinmugo.com/posts/sunshine-run-half-marathon/</link><guid isPermaLink="true">https://jinmugo.com/posts/sunshine-run-half-marathon/</guid><description>변화무쌍한 날씨와 후반 정강이 통증 속에서 처음 공식 하프마라톤을 완주하며 배운 페이스와 욕심에 관한 기록.</description><pubDate>Fri, 05 Sep 2025 02:06:49 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;p&gt;지난 8월 30일에 열린 제4회 선샤인 런에 참가했다. 달리기를 시작한 이래로, 처음 공식적으로 뛰어보는 하프이기도 하고 이래저래 기억에 많이 남았던 마라톤이어서 글로 남겨보려 한다.&lt;/p&gt;
&lt;p&gt;우선, 날씨 얘기를 안 할 수가 없을 것 같다. 글을 읽는 당신이 기억할지 모르겠지만, 대회 당일 2025년 8월 30일의 날씨는 정말 변덕 그 자체였다. 내가 러닝을 하는 2시간 내외의 짧은 시간 동안 날씨가 몇 번이나 바뀌었는지 모르겠다. 출발할 때는 비가 올 것 같은 날씨였는데, 뛰는 중간에 해가 떠오르더니 어느샌가 자취를 감추고 다시 비가 오고…. 정말 신기한 날씨였다. 무지개도 뜨고.
다행인 건 나는 비 오는 날뛰는 우중런을 굉장히 좋아한다. 나중에 신발 빨긴 힘들지만, 숨쉬기도 편하고 비에 젖은 나무들이 더 예뻐 보일 때가 있으니까.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;마라톤은 잠실 종합운동장 9시에 출발이었고 춘천에서 아침 버스를 타고 출발 지점까지 지하철로 이동했다. 미리 시간 계산을 하고 갔지만 생각보다 시간이 많지 않았다. 그래서 충분히 몸을 풀지 못하고 출발선에 서게 되었다. 그 때문인지 얼마 지나지 않아 몸이 생각보다 더 처지고 무겁게 느껴졌다. 그래도 어쩌겠는가. 이미 레이스는 시작되었다. 다행인 건 이번에는 혼자 뛰는 게 아니라 사람들과 함께 뛴다는 것이다.&lt;/p&gt;
&lt;p&gt;혼자 하프를 뛸 때와 같이 뛸 때의 차이점은 서로서로 보면서 뛴다는 것이다. 레이스 초반에는 생각이 많았다. 다른 사람들의 페이스를 쫓아가 보기도 하고 주법들을 유심히 살펴보면서 내가 뛰는 방법과 어떤 게 다를까. 저 사람은 어떻게 저렇게 잘 달릴까? 등등…. 다른 사람들을 바라보는 동시에 나를 봤다.&lt;/p&gt;
&lt;p&gt;중반으로 접어들자, 이제는 다른 사람들 생각을 조금 덜 하기 시작했다. 아니, 할 수 없었다고 말하는 게 좋겠다. 내가 당장 힘들어서. 숨이 차고 다리가 아파지니 다른 사람들을 볼 여력이 없었다. 그렇게 무심히 초록을 지나고 파랑을 넘어 나갔다.
이번 마라톤의 목표는 한 번도 걷지 않는 것이었는데, 도저히 지킬 수가 없었다. 마라톤 코스 중간에 있는 꽤 높은 언덕에 한 방 먹고 멈출 수밖에 없었다. 오르막길이 있으면 내리막길도 있다. 단순한 진리다. 그리고 나는 그 사실이 한순간 미워졌다. 내리막길도 조절하지 않으면 가다가 지칠 수 있다. 조심해야 한다.&lt;/p&gt;
&lt;p&gt;15km 지점을 지나고 나서 점점 몸이 한계에 다다르기 시작했다. 평소대로라면 이 정도 거리를 지나고도 쌩쌩했겠지만, 오늘은 왜인지 달랐다. 특히 오른쪽 정강이 쪽이 쥐가 날듯이 저렸다. 가다가 쉬고 가다가 쉬고를 반복하며 나아갔다.
뛰다 걸으면 보이지 않던 게 보이기 시작한다. 특히 눈에 띄는 주자들이 보인다. 나와 비슷한 또래들 나이가 들어 보이는 중년들 그리고 아이들까지. 절로 겸손해지는 세상이다. 참 넓고 많은 사람이 있음을 새삼 느끼게 된다.&lt;/p&gt;
&lt;p&gt;체감상 18km 구간이 가장 힘들었다. 코스를 한 바퀴 돌아 처음 가볍게 지나쳤던 3km 지점을 15km의 간극을 두고 다시 마주했을 때는 싱숭생숭한 기분이었다. 마지막 1km에선 오른쪽 정강이에 쥐가 난 채로 뛰었다. 적어도 2시간 내에는 도착하고 싶었는데 도저히 힘을 낼 수 없어 아쉽게 포기했다.
하프 마라톤은 참 묘한 거리다. 10km보다 분명히 길고, 전 구간보다는 가깝다. 그래서 욕심을 부리기 쉬운 거리이기도 하다. 하프의 매력은 바로 거기에 있다. 내 한계와 욕심이 가장 자주 마주치는 지점. 그날의 컨디션, 날씨, 관중의 응원, 누군가의 발걸음, 내 호흡—모든 것이 결과에 영향을 준다. 그렇기에 매번 새로운 레이스가 된다. 언젠가 무라카미 하루키의 &amp;lt;달리기를 말할 때 내가 하고 싶은 이야기&amp;gt;에서 이런 비슷한 구절을 읽은 것 같기도 하다. 매번 똑같은 길을 똑같이 뛰어가고 있더라도 그날의 러닝은 그 어떤 달리기와도 다른 방향으로 달려가고 있을 것이라고. 내 기억이 완벽하진 않은 것 같지만 그래도 뭐 어떤가.&lt;/p&gt;
&lt;p&gt;혹시 지금 첫 하프를 고민하고 있다면, 한번 뛰어보는 걸 권한다. 불완전한 조건 속에서도 꾸준히 앞으로 나아가는 마음으로. 이번 제4회 선샤인 런은 내 러닝 인생에 굵은 줄로 남을 것 같다. 이제 시작이다. 세상의 모든 마라톤을 제패할 때까지 파이팅.&lt;/p&gt;
</content:encoded><category>marathon</category><category>running</category><category>sunshine-run</category><category>half-marathon</category><author>Jinmu Go</author></item><item><title>The mind behind linux</title><link>https://jinmugo.com/posts/the-mind-behind-linux/</link><guid isPermaLink="true">https://jinmugo.com/posts/the-mind-behind-linux/</guid><description>오픈 소스에 대하여</description><pubDate>Fri, 19 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;https://youtu.be/o8NPllzkFhE?si=iX4O4Z0duW7-5Q7s&lt;/p&gt;
&lt;p&gt;처음 개발을 배웠던 당시, &quot;오픈 소스&quot;라는 신기한 문화에 적응하는데 꽤 애를 먹었던 것 같다. 처음 이 개념을 접했을 당시의 기억이 떠오른다. 내가 몸담고 있던 영화 영상은 각자의 편집이나 촬영 방식이 그 사람의 아이덴티티가 될 수 있었다.
그래서 나를 포함한 일부의 사람들은 자신의 이러한 기술들을 다른 사람들과 쉽게 공유하지 않고 본인만의 것으로 숨겼다. 숨겼다기보다는 서로 쉽게 말하지 않았다는 표현이 적절하겠다.
숨기지는 않았지만 이런 기술에 대한 이야기나 편집 같은 걸 이야기하기에는 무언가 껄끄러운 기분이었다. 비단 영화 영상뿐만이 아니라 주변의 다른 학과 친구들에게 물어보아도 비슷했던 것 같다. 상대방에게 물어보면 어느 정도 알려주긴 하지만 모든 것을 쉽게 알려주지 않았다. 또한 이러한 정보를 얻을 수 있는 &quot;커뮤니티&quot;도 현저히 적었다. 그때는 겨우 선배님들 에게 물어보아서 배웠던 걸로 기억한다.&lt;/p&gt;
&lt;p&gt;그래서 프로그래밍을 처음 입문했을 당시에 인터넷 상에 널려있는 많은 코드들을 보고 놀라지 않을 수 없었다. 많은 사람들이 자신의 시간과 노력이 들어간 코드를 인터넷에 누구나 열람할 수 있도록 공개하고 있었고, 다른 프로그래머들은 해당 코드를 사용해서
본인의 프로젝트를 진행한다. 그러던 중에 무언가 해당 코드가 개선할 점이 있다면 자신이 직접 고쳐서 기여 하기도 한다. 물론, 기여자와 해당 코드의 원작자는 서로를 알지 못하는 상태이다.&lt;/p&gt;
&lt;p&gt;이런 특이한 문화는 어떻게 형성된 것일까? 폐쇄적이고 내 기술을 숨기려고 했던 사람들을 어떻게 &quot;오픈소스&quot;는 모두를 오픈 마인드로 바꿀 수 있었을까. 여러가지 영향들이 있었지만 그중에 큰 역할을 했던 것이 Linus Torvalds(이하 토발즈)의 Linux이다. 토발즈는 본인이 1년 반 가까이 진행했던 프로젝트인 Linux의 코드를 인터넷에 공개했다. 처음엔 그도 내가 공들여 만든 작품을 이용해 누군가 이득을 보지 않을까 고민했지만 더 이상 생각하지 않고 &quot;해보지 뭐&quot;라는 생각으로 시작했다고 한다.&lt;/p&gt;
&lt;p&gt;이런 작은 순간이 리눅스의 시작이었다. 그리고 주변에서 사람들이 리눅스에 관심을 가지고 아이디어와 코드 리뷰를 해주면서 어느 순간 리눅스는 역대 최대 규모의 소프트웨어 프로젝트가 되었다. 영상에서 토발즈는 말했다. &quot;제게 진짜 중요한 순간은, 뭔가가 크게 되었을 때가 아니라 작게 될 때입니다.&quot; 토발즈는 아주 작은 것들의 힘을 알았던 것 같다. 지금에서야 리눅스는 아주 큰 프로젝트이지만, 초창기에 한 명 한 명이 아이디어를 제공하지 않았다면 지금의 상황에 오지 않았을 것이기 때문이다.&lt;/p&gt;
&lt;p&gt;리눅스를 기점으로 전 세계에서 수많은 소프트웨어들이 오픈 소스로 개발되고 있다. 리눅스를 비롯한 mysql, git, php, swift,node.js 등이 그 예시이다. 우리가 오늘날 사용하고 있는 소프트웨어 중 오픈 소스로 개발된 기술을 사용하지 않는 것은 전무하다고 보아도 무방하다. 세계각국의 다양한 사람들의 시점을 공유하고 그들이 정말 열심히 노력해서 만든 코드를 무상으로 볼 수 있다는 건 너무나 큰 행운이다.&lt;/p&gt;
&lt;p&gt;오픈 소스의 철학은 계속해서 확장되고 있다. 위키피디아, 나무위키 같은 웹 백과사전들이 만들어졌다. 백과사전은 전문가, 기업에서 만들어지는 어려운 것이라는 생각에서 벗어나 누구나 자신의 지식을 작성하고 또 다른 사람들이 해당 지식을 더 정확하게 수정하면서 발전해 나갔다. 앞으로 오픈 소스는 어떤 길을 걷게 될까. 궁금해진다. 영상에서 진행자가 이렇게 묻는다. &quot;오픈소스 아이디어는 세상에 충분히 실현되었습니까? 아니면 아직 가야할 길이 남아있습니까? 라고.&lt;/p&gt;
&lt;p&gt;토발즈는 대답한다. &quot;오픈 소스는 비단 프로그래밍을 넘어서 과학 저널이나 백과사전에도 영향을 끼쳤다. 나는 선지자가 아니고 새로운 것을 이야기하는 것은 내 몫이 아니다.&quot;&lt;/p&gt;
</content:encoded><category>리눅스</category><category>리누스 토르발스</category><category>오픈소스</category><author>Jinmu Go</author></item><item><title>트라이(Trie) - feat leetcode 208. Implement Trie</title><link>https://jinmugo.com/posts/trie-feat-leetcode-208/</link><guid isPermaLink="true">https://jinmugo.com/posts/trie-feat-leetcode-208/</guid><description>re&quot;trie&quot;val</description><pubDate>Thu, 02 Jul 2026 06:16:59 GMT</pubDate><content:encoded>&lt;p&gt;leetcode 208. Implement Trie 문제를 푸는김에 정리해보는 Trie.&lt;/p&gt;
&lt;p&gt;트라이(Trie)는 &lt;strong&gt;문자열 탐색에 특화된 트리 기반의 자료구조&lt;/strong&gt;다. &quot;re&lt;strong&gt;trie&lt;/strong&gt;val(검색)&quot;에서 이름을 따왔으며, 접두사(prefix) 검색이 필요한 문제에서 자주 사용된다.
문자열을 저장하는 가장 단순한 방법은 배열이나 해시맵을 사용하는 것이다.&lt;/p&gt;
&lt;p&gt;예를 들어, 자동완성 기능이 있겠다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;알고리&quot;  →  알고리즘, 알고리즘 문제, 알고리즘 공부법 ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 기능을 구현하기 위해서 가장 단순한 접근은 모든 단어를 배열에 담아두고, 하나하나 비교하는 선형 탐색이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[&quot;apple&quot;, &quot;app&quot;, &quot;april&quot;, &quot;apply&quot;, ...] 중에서
&quot;app&quot;으로 시작하는 단어만 골라내기 → 전부 순회하면서 확인
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;단어가 10개만 있다면 문제 없겠지만, 사전에 등록된 단어가 수십만 개라면? 검색어를 입력할 때마다 전체 목록을 훑는 건 너무 느리다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;해시맵(HashMap)&lt;/strong&gt; 은 어떨까? 해시맵에 단어를 통째로 저장하면 O(1)의 시간복잡도에 해결된다. 하지만 해시맵은 정확히 일치하는 단어만 찾을 수 있다. &quot;app&quot;이라는 접두사로 시작하는 모든 단어를 찾으려한다면? 다시 전체 순회를 할 수 밖에 없다.&lt;/p&gt;
&lt;p&gt;이때 등장하는 아이디어가 트라이(Trie)이다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방식&lt;/th&gt;
&lt;th&gt;검색(Search)&lt;/th&gt;
&lt;th&gt;접두사 검색(Prefix Search)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;배열 + 선형 탐색&lt;/td&gt;
&lt;td&gt;O(N * L)&lt;/td&gt;
&lt;td&gt;O(N * L)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;해시맵&lt;/td&gt;
&lt;td&gt;O(L) (평균)&lt;/td&gt;
&lt;td&gt;O(N * L)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trie&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(L)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(L)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;N = 저장된 단어의 수, L = 문자열의 길이&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Trie는 접두사 검색에서도 O(L)을 유지한다&lt;/strong&gt;는 점이 핵심이다.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;트라이는 노드(Node)들의 연결로 이루어진 트리 구조다. 일반 트리와의 결정적인 차이는 다음과 같다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;일반 트리&lt;/strong&gt;: 하나의 노드에 데이터 전체(단어 하나)를 저장한다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;트라이&lt;/strong&gt;: 하나의 노드에 &lt;strong&gt;문자 한 글자&lt;/strong&gt;만 저장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;트라이의 노드는 일반적으로 두 가지 정보를 가진다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필드&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;다음 문자로 연결되는 자식 노드들에 대한 참조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IsEnd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;이 노드가 하나의 단어 끝인지를 나타내는 플래그&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;예를 들어 &lt;code&gt;&quot;app&quot;&lt;/code&gt;과 &lt;code&gt;&quot;apple&quot;&lt;/code&gt; 두 단어를 트라이에 저장하면 아래와 같은 구조가 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;        (루트)
          |
          a
          |
          p
          |
          p  ← IsEnd = true  (&quot;app&quot; 완성)
          |
          l
          |
          e  ← IsEnd = true  (&quot;apple&quot; 완성)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&quot;app&quot;&lt;/code&gt;과 &lt;code&gt;&quot;apple&quot;&lt;/code&gt;은 접두사 &lt;code&gt;&quot;app&quot;&lt;/code&gt;을 공유하므로, 해당 구간의 노드는 중복되지 않는다. 이것이 트라이의 핵심적인 특징이다.&lt;/p&gt;
&lt;p&gt;트라이(Trie)의 가장 기본적인 세가지 동작은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Insert (삽입)&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;루트 노드에서 시작한다.&lt;/li&gt;
&lt;li&gt;단어의 첫 글자부터 순서대로 자식 노드가 있는지 확인한다.
&lt;ul&gt;
&lt;li&gt;없으면: 새 노드를 만들어 추가한다.&lt;/li&gt;
&lt;li&gt;있으면: 해당 노드로 이동한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단어의 마지막 글자까지 도달하면 해당 노드의 &lt;code&gt;IsEnd&lt;/code&gt;를 &lt;code&gt;true&lt;/code&gt;로 설정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Search (탐색)&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;루트 노드에서 시작한다.&lt;/li&gt;
&lt;li&gt;단어의 글자를 따라 노드를 이동한다.
&lt;ul&gt;
&lt;li&gt;도중에 자식 노드가 없으면 → &lt;code&gt;false&lt;/code&gt; 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단어 끝까지 도달하면 &lt;code&gt;IsEnd&lt;/code&gt; 값을 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;StartsWith (접두사 검색)&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Search와 동일하게 단어(접두사)를 따라 내려간다.&lt;/li&gt;
&lt;li&gt;단어 끝까지 도달했는지만 확인한다. &lt;code&gt;IsEnd&lt;/code&gt;는 확인하지 않는다.
&lt;ul&gt;
&lt;li&gt;즉, 해당 접두사로 시작하는 단어가 하나라도 존재하면 &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;StartsWith&lt;/code&gt;의 존재가 트라이를 배열이나 해시맵과 구분 짓는 핵심이다. 접두사만으로도 O(L)에 존재 여부를 판단할 수 있다.&lt;/p&gt;
&lt;p&gt;아래는 golang으로  풀어본 &lt;a href=&quot;https://leetcode.com/problems/implement-trie-prefix-tree/description/&quot;&gt;208번 leetcode문제&lt;/a&gt;이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type TrieNode struct {
    Children map[rune] *TrieNode
    IsEnd bool
}

func NewTrieNode() *TrieNode {
    return &amp;amp;TrieNode {
        Children: make(map[rune]*TrieNode),
        IsEnd: false,
    }
}

type Trie struct {
    Root *TrieNode
}

func Constructor() Trie {
    return Trie{
        Root: NewTrieNode(),
    }
}

func (this *Trie) Insert(word string)  {
    current := this.Root

    for _, char := range word {
        if _, exists := current.Children[char]; !exists {
            current.Children[char] = NewTrieNode()
        }

        current = current.Children[char]
    }

    current.IsEnd = true
}

func (this *Trie) Search(word string) bool {
    current := this.Root

    for _, char := range word {
        if nextNode, exists := current.Children[char]; exists {
            current = nextNode
        } else {
            return false
        }
    }

    return current.IsEnd
}

func (this *Trie) StartsWith(prefix string) bool {
    current := this.Root

    for _, char := range prefix {
        if nextNode, exists := current.Children[char]; exists {
            current = nextNode
        } else {
            return false
        }
    }

    return true
}


/**
 * Your Trie object will be instantiated and called as such:
 * obj := Constructor();
 * obj.Insert(word);
 * param_2 := obj.Search(word);
 * param_3 := obj.StartsWith(prefix);
 */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;트라이는 접두사 검색을 O(L)에 처리할 수 있다는 점에서 다음 같은 상황에서 유용하게 사용된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;자동완성 (Autocomplete)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;사용자가 입력한 접두사로 시작하는 모든 단어를 찾아 제안해야 한다. 트라이는 &lt;code&gt;StartsWith&lt;/code&gt;으로 접두사 존재 여부를 확인한 뒤, 해당 노드 아래의 모든 &lt;code&gt;IsEnd&lt;/code&gt; 노드를 탐색하여 후보 목록을 반환한다. 해시맵으로는 전체 단어 목록을 순회해야 하지만, 트라이는 접두사에 해당하는 하위 트리(subtree)만 탐색하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;맞춤법 검사 (Spell Checker)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;주어진 단어가 사전에 존재하는지 빠르게 확인하는 데 사용된다. &lt;code&gt;Search&lt;/code&gt; 연산을 O(L)에 처리할 수 있으며, 사전에 등록된 단어가 많을수록 배열이나 해시맵 대비 효율이 좋아진다. 또한 유사한 단어 추천(Levenshtein distance 기반)을 구현할 때도 트라이 탐색을 함께 사용하기도 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IP 라우팅 (Longest Prefix Match)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;네트워크에서 IP 주소를 라우팅할 때, 목적지 IP와 가장 길게 일치하는 prefix를 찾아야 한다. 트라이는 이진 트라이(binary trie) 또는 Patricia trie 형태로 변형되어 사용되며, prefix 기반의 최장 일치 검색에 최적화되어 있다.&lt;/p&gt;
&lt;p&gt;트라이는 문자열 탐색, 특히 &lt;strong&gt;접두사 검색(prefix search)&lt;/strong&gt; 을 O(L)에 해결해야 하는 상황에서 가장 빛을 발하는 자료구조다. 노드 하나에 문자 한 글자만 저장하고, 공통 접두사를 공유함으로써 탐색 효율을 얻는 대신 공간을 소비한다. 앞서 살펴본 자료구조들과의 차이를 다시 정리하면 다음과 같다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;자료구조&lt;/th&gt;
&lt;th&gt;검색(Search)&lt;/th&gt;
&lt;th&gt;접두사 검색&lt;/th&gt;
&lt;th&gt;주요 단점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;배열 + 선형 탐색&lt;/td&gt;
&lt;td&gt;O(N·L)&lt;/td&gt;
&lt;td&gt;O(N·L)&lt;/td&gt;
&lt;td&gt;느린 탐색&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;해시맵&lt;/td&gt;
&lt;td&gt;O(L)&lt;/td&gt;
&lt;td&gt;O(N·L)&lt;/td&gt;
&lt;td&gt;prefix 검색 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trie&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(L)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;O(L)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;많은 메모리 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;접두사 검색이 필요한가?&lt;/strong&gt; → Trie&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;정확한 일치(Exact Match)만 필요한가?&lt;/strong&gt; → 해시맵&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;데이터가 적은가?&lt;/strong&gt; → 배열로도 충분하다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;시간이 된다면 아래 문제들도 풀어보길 권한다.&lt;/p&gt;
&lt;p&gt;LeetCode 208. &lt;a href=&quot;https://leetcode.com/problems/implement-trie-prefix-tree/&quot;&gt;Implement Trie (Prefix Tree)&lt;/a&gt;
LeetCode 1268. &lt;a href=&quot;https://leetcode.com/problems/search-suggestions-system/&quot;&gt;Search Suggestions System&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>trie</category><author>Jinmu Go</author></item><item><title>매직 패킷은 왜 MAC주소를 16번 반복할까?</title><link>https://jinmugo.com/posts/wake-on-lan-mac-address-16-times/</link><guid isPermaLink="true">https://jinmugo.com/posts/wake-on-lan-mac-address-16-times/</guid><description>Wake-on-LAN 매직 패킷이 FF 6바이트와 대상 MAC 주소 16회 반복으로 구성되는 이유를 프레임 구조와 탐지 방식에서 살펴봅니다.</description><pubDate>Sun, 17 Aug 2025 12:25:50 GMT</pubDate><content:encoded>&lt;p&gt;최근 사용하지 않는 컴퓨터를 이용해서 proxmox 서버를 구축했다. 재밌게 홈서버를 구축하던 도중 문득, &quot;원격으로 컴퓨터를 끄는 건 ssh 연결해서 &lt;code&gt;shutdown&lt;/code&gt;이나 &lt;code&gt;halt&lt;/code&gt;, &lt;code&gt;poweroff&lt;/code&gt;같은 걸 사용할 수 있는데 꺼져 있는 컴퓨터를 원격으로 켜는 것도 가능할까?&quot; 라는 생각이 들었다.&lt;/p&gt;
&lt;p&gt;그래서 알아봤더니  &lt;code&gt;WoL(Wake-on-LAN)&lt;/code&gt;이라는 기술을 통해 구현할 수 있다고 해서 알아보던 도중 굉장히 궁금하게 생긴 부분을 발견해서 블로그에 간단히 적어보려 한다.&lt;/p&gt;
&lt;p&gt;WoL은 네트워크를 통해 저전력 상태의 컴퓨터에 신호를 보내 깨우는 기술이다. 그리고 이를 가능하게 하는 것이 &lt;strong&gt;매직 패킷(Magic Packet)&lt;/strong&gt; 이라는 데이터 조각인데, 매직 패킷의 구조를 들여다 보면 컴퓨터의 고유 주소인 &lt;strong&gt;MAC주소를 무려 16번이나 똑같이 반복&lt;/strong&gt;해서 보낸다. 4번 8번도 아니고 16번을 보내는 이런 신기한 숫자가 어떻게 나왔는지 궁금해서 그 이유를 파헤쳐 보려 한다.&lt;/p&gt;
&lt;h2&gt;WoL과 매직 패킷&lt;/h2&gt;
&lt;p&gt;먼저 &lt;strong&gt;WoL(Wake on LAN)&lt;/strong&gt; 과  &lt;strong&gt;매직 패킷(Magic Packet)&lt;/strong&gt; 이 무엇인지 간단히 짚고 넘어가보자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;WoL(Wake on LAN)&lt;/strong&gt;: WoL은 매직 패킷 기술에 부여된 프로토콜 이름이다.  이 기술은 원격으로 전원이 꺼져 있거나 절전 모드에 있는 PC를 네트워크를 통해 깨우는데 사용된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;매직 패킷(Magic Packet)&lt;/strong&gt;: 특수한 데이터 패턴을 가진 이더넷 프레임. 이 패킷은 수신 이더넷 컨트롤러에 의해 감지되어 컴퓨터를 꺠우는 역할을 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;매직 패킷(이더넷 프레임)의 구조&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;앞서 말했듯이 매직 패킷은 특수한 데이터 패턴을 가진 &lt;strong&gt;이더넷 프레임&lt;/strong&gt;이다. 매직 패킷의 구조를 이해하기 위해서는 이더넷 프레임에 대해 알아야 한다.&lt;/p&gt;
&lt;p&gt;이더넷 프레임은 총 5가지 필드로 정의 된다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MAC dest : 목적지 MAC 주소. 이 패킷을 수신할 네트워크 장비의 물리적 주소를 의미한다. 보통 네트워크 내의 모든 장치에 신호를 보내기 위해 브로드캐스트 주소 &lt;code&gt;FF:FF:FF:FF:FF:FF&lt;/code&gt;가 사용된다.&lt;/li&gt;
&lt;li&gt;MAC src: 출발지 MAC 주소. 이 패킷을 보내는 장치의 MAC 주소이다.&lt;/li&gt;
&lt;li&gt;MISC(기타등등): 이더넷 프레임의 유형을 나타내는 이더타입(EtherType)등이 포함되는 영역이다. 이 패킷이 어떤 종류의 데이터인지를 알려주는 역할을 한다.&lt;/li&gt;
&lt;li&gt;Data / Payload: 실제 전송하려는 데이터가 담기는 부분이다. 상위 계층(ex: TCP/IP)의 헤더와 데이터가 여기에 포함된다. 만약 데이터의 크기가 46바이트보다 작을 경우, 패딩을 추가하여 최소 크기를 맞춘다.&lt;/li&gt;
&lt;li&gt;CRC(Cyclic Redundancy Check): 프레임 검사 시퀀스로 전송 중에 발생할 수 있는 오류를 검출하기 위한 필드이다. 해시 알고리즘 비슷하게 무결성을 확인하는 것 같은데 자세한 건 나중에 조금 더 알아보자.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;[!note]+ 이더넷 프레임의 패딩
초창기 이더넷은 모든 컴퓨터가 하나의 통신 회선을 공유하는 방식이었고 이때 &lt;strong&gt;CSMA/CD&lt;/strong&gt;라는 규칙에 따라 통신했다. 이때 CD가 Collision Detection인데 만약 두 컴퓨터가 동시에 데이터를 보내서 &apos;충돌&apos;이 발생하면, 이 충돌을 발신 측에서 감지하고 데이터 전송을 멈춘 뒤 다시 보냈다.
이더넷 프레임의 데이터가 46바이트보다 작을 때 46바이트를 채워야 하는 이유는 충돌 감지(Collision Dectection)때문이다. 데이터 패킷이 너무 작으면 네트워크에서 데이터 충돌이 발생해도 발신 측이 이를 알아채지 못하고 다시 보내지 않고 지나가는 문제가 생길 수 있었다.
충돌이 발생한 데이터는 다시 발신 측에 되돌아오게 되는데, 만약 A가 너무 작은 패킷을 보내 순식간에 전송을 끝내버리면 A는 데이터가 깨졌다는 사실을 모르고 데이터는 유실되고 만다.
따라서 엔지니어들은 이런 시나리오에서도 충돌을 확실히 감지할 수 있도록, 패딩을 추가하여 임의로 데이터의 길이를 늘이는 방법을 택했다. 이때 정의된 크기가 바로 64바이트였고 프레임의 기본 정보 18바이트를 제외하고 실제 데이터의 최소 크기 (64 - 18)를 46바이트로 산정한 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Magic Pattern&lt;/h3&gt;
&lt;p&gt;그림에 나와 있는 &lt;strong&gt;MAGIC Pattern&lt;/strong&gt;은 Payload 필드에 담기게 된다. 매직 패턴은 총 102바이트로 구성되며 크게 두 부분으로 나눌 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;FF ... FF (6 bytes): 동기화 스트림(Synchronization Stream)이라고 부르며 &lt;code&gt;FF&lt;/code&gt;값이 6번 연속으로 나타나는데, 이 스트림은 수신측의 이더넷 컨트롤러가 매직 패킷을 쉽게 스캔하고 감지하도록 돕는다.&lt;/li&gt;
&lt;li&gt;MAC 6 * 16 (96bytes): &lt;strong&gt;깨우고자 하는 MAC주소를 16번 반복&lt;/strong&gt;한 부분이다. 이부분이 매직 패턴의 핵심이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;추가로 Secure On Password로 6bytes크기의 비밀번호를 설정할 수 있는데, 일부 WoL기술에서 보안을 강화하기 위해 사용하는 영역이다. 기능이 활성화된 경우에만 사용되며, 허가되지 않은 사용자가 컴퓨터를 원격으로 켜는 것을 방지한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!note]+ 동기화 스트림이 어떻게 쉽게 감지를 돕는가.
동기화 스트림은 스캔 상태 머신(scanning state machine)을 훨씬 단순하게 만들어준다. 스캔 상태 머신은 이더넷 컨트롤러 내부의 하드웨어 로직을 의미하는데, 이는 들어오는 데이터를 분석하여 특정 패턴을 찾는 역할을 한다.
만약 동기화 스트림이 없다면, 이더넷 컨트롤러는 들어오는 모든 바이트 시퀀스에서 언제 6바이트 MAC 주소가 16번 반복되는 패턴이 시작될지 계속해서 확인해야 한다. 이는 보다 더 복잡하고 전력 소모가 큰 작업이 된다.
하지만 FF ... FF라는 명확하고 독특한 동기화 스트림이 먼저 오면, 컨트롤러는 이 6바이트 시퀀스가 감지되었을 때 비로소 뒤이어 오는 MAC 주소 패턴을 스캔하기 시작할 수 있다. 즉, 동기화 스트림은 일종의 신호탄 혹은 표식 역할을 하여, 컨트롤러가 불필요한 스캔 작업을 최소화하고 필요한 시점에만 집중적으로 패턴을 찾도록 돕는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;사족이 길었지만, 이제 왜 MAC주소를 16번이나 길게 늘여 보내는지 알아 보자.&lt;/p&gt;
&lt;h2&gt;왜 MAC주소를 16번이나 반복할까?&lt;/h2&gt;
&lt;p&gt;이에 대한 해답을 찾아 국내 글들을 찾아다녔지만, 이런 패킷을 보내면 이렇게 됩니다! 와 같이 나올 뿐 해답이 없었고 Gemini한테 물어봐도 아래와 이상한 답변한 할 뿐이었다.&lt;/p&gt;
&lt;p&gt;참고로 질문은 &lt;strong&gt;매직 패킷에서 왜 mac 주소를 다른 숫자도 아니고 16번 반복하는 거야?&lt;/strong&gt; 이렇게 질문했다.&lt;/p&gt;
&lt;p&gt;
&lt;s&gt;박수짝짝&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;그러던 중 serverfault라는 사이트의 2014년 글을 보고 어느정도 이 물음에 대한 해답이 되었다. 사실 공식 문서도 아니고 정확한 정보도 아니지만 그래도 답변자의 논리에 내가 납득할 수 있었기 때문에 글을 가져와 봤다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;Why does a Wake-On-LAN packet contain 16 duplications of the target MAC address?&quot;
image: &quot;https://cdn.sstatic.net/Sites/serverfault/Img/apple-touch-icon@2.png?v=9b1f48ae296b&quot;
description: &quot;From the Wireshark webpage: The Target MAC block contains 16 duplications of the IEEE address of the target, with no breaks or interruptions. Is there any specific reason for the 16 duplications?&quot;
url: &quot;https://serverfault.com/questions/632908/why-does-a-wake-on-lan-packet-contain-16-duplications-of-the-target-mac-address&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;핵심은 구현의 &lt;strong&gt;단순성&lt;/strong&gt;과 &lt;strong&gt;신뢰성&lt;/strong&gt;이다.&lt;/p&gt;
&lt;p&gt;처음 매직 패킷을 만들던 AMD와 HP(Hewltt Packard)는 이 기능을 기존 하드웨어의 구성을 최대한 건들지 않고 추가하는 것을 목표로 했다. 따라서 이미 이더넷 컨트롤러에 존재하는 &lt;strong&gt;주소 일치 회로&lt;/strong&gt;를 매직 패킷 감지에도 활용하기로 하였다.
주소 일치 회로는 네트워크상에서 들어오는 수많은 이더넷 프레임 중에서 &lt;strong&gt;해당 PC의 MAC 주소와 일치하는 프레임만을 식별하여 수신&lt;/strong&gt;하는 역할을 한다. 매직 패킷을 감지하는 기능을 추가하기 위해서는 단순히 MAC 주소를 16번 반복하여 세는 카운터를 추가하면 되었다.&lt;/p&gt;
&lt;p&gt;그래서 0부터 15까지의 값을 담을 수 있는 4비트 카운터를 이용하여, 카운터가 오버플로우(15+1=0)될 때 깨우기 코드를 실행하도록 하는 단순한 로직을 가능하게 한다. 위 링크의 pseudo code에 따르면 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Initialize a single counter that holds values from 0-15.
Set it to 0.
Watch the network. When I see the signal:
Loop:
  Do I see my address at the right spot?
  Yes: Add 1 to counter.
    Did I just overflow? (15+1 = 0?)
    Yes: Jump out of loop to &quot;wake up&quot; code.
...otherwise
Loop again.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기까지가 구현의 단순성에 관한 것이고 &lt;strong&gt;16이라는 숫자는 구현의 신뢰성에서 기인&lt;/strong&gt;한다. 앞서 말했듯이 인터넷은 수많은 정보가 왔다 갔다 하는 상황이다. 이런 상황에서 무작위 데이터 스트림이 시스템을 켜는 명령이 되는 것을 방지하기 위해서는 &lt;strong&gt;쉽게 식별할 수 있어야 하며 오탐 가능성이 거의 없어야&lt;/strong&gt; 한다.&lt;/p&gt;
&lt;h3&gt;만일 2비트 카운터를 사용한다면?&lt;/h3&gt;
&lt;p&gt;만일 2비트 카운터를 사용하여 4번만 MAC 주소를 반복한다면 24 bytes이므로 프레임 데이터의 최소 크기(46bytes) 보다 작아 패딩을 추가하게 된다. 이렇게 된다면 우연히 해당 시퀀스가 네트워크에서 발생할 확률이 훨씬 높아져 &lt;strong&gt;시스템이 의도치 않게 깨어날 가능성이 크게 증가했을 것&lt;/strong&gt;이다. 또한 &lt;strong&gt;매직 패킷이 다른 네트워크 트래픽과 구별되는 특성이 약화&lt;/strong&gt;되어 신뢰성이 저하될 수 있다.
요약하자면, 2비트 카운터를 사용하여 MAC 주소를 4번만 반복하게 된다면 하드웨어 구현의 단순성 자체는 유지될 수 있었겠지만, 중요한 목표 중 하나인 &lt;strong&gt;&quot;오작동 방지&quot;&lt;/strong&gt; 와 &lt;strong&gt;&quot;높은 신뢰성&quot;&lt;/strong&gt; 측면에서 현재의 16번 반복 방식보다 현저히 불리했을 것이다.&lt;/p&gt;
&lt;h3&gt;만일 8비트 카운터를 사용한다면?&lt;/h3&gt;
&lt;p&gt;8비트 카운터는 0부터 255까지의 값을 표현할 수 있고 카운터가 오버플로우(255+1=0)되도록 하려면 MAC 주소는 256번 반복되어야 한다. MAC 주소는 6바이트이므로, 256번 반복되면 &lt;strong&gt;1,536바이트&lt;/strong&gt;가 된다. 이는 이더넷 프레임의 최대 크기를 초과한 값이므로 실제 구현이 불가능하며 만일 가능하다 하더라도 프레임의 크기가 불필요하게 커져 무의미하게 많은 데이터를 옮겨야 했을 것이다.&lt;/p&gt;
&lt;h3&gt;4비트 카운터&lt;/h3&gt;
&lt;p&gt;그렇다면 왜 4비트 카운터였을까? 앞서 살펴본 가설들을 종합해보자&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;2비트 카운터(4번 반복)는 너무 위험하다.&lt;/strong&gt; 페이로드 크기가 너무 작아 오탐(false positive)의 가능성을 높인다. 네트워크상의 데이터가 우연히 매직 패킷으로 인식되어 시스템이 원치 않게 켜질 위험이 있었다. &lt;strong&gt;신뢰성&lt;/strong&gt; 측면에서 탈락.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;8비트 카운터(256번 반복)는 너무 비현실적이다.&lt;/strong&gt; 페이로드 크기가 이더넷 최대 전송 단위(MTU)를 초과해버려 애초에 표준 네트워크 환경에서 전송 자체가 불가능하다. &lt;strong&gt;실용성&lt;/strong&gt; 측면에서 탈락.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;결국 &lt;strong&gt;4비트 카운터(16번 반복)&lt;/strong&gt; 는 이 두 가지 문제점을 모두 해결하는 최적의 지점이었다. 96바이트라는 충분한 길이를 확보하여 &lt;strong&gt;신뢰성&lt;/strong&gt;을 극대화하면서도, 이더넷 프레임 표준을 완벽히 준수하여 &lt;strong&gt;실용성&lt;/strong&gt;을 잃지 않았다. 동시에 4비트라는 단순한 카운터로 구현할 수 있어 &lt;strong&gt;하드웨어의 복잡성&lt;/strong&gt;도 최소화했다.&lt;/p&gt;
&lt;p&gt;이처럼 주어진 제약 조건 안에서 안정성과 효율성, 그리고 단순성까지 모두 잡아낸 숫자가 16이었던 것이다.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;단순히 홈서버를 구성하며 시작된 16이라는 숫자에 대한 호기심에서 비롯되어 여기까지 와버렸다. 초창기 이더넷 컨트롤러의 제한된 자원, 최소 프레임 크기, 그리고 무작위 데이터와 신호를 구분해야 하는 여러 제약 속에서 엔지니어들은 해법을 찾아내었다.
사소해 보이는 기술적 디테일 하나에도 이처럼 합리적인 이유와  해법이 담겨 있다는 사실이, 엔지니어링의 진정한 매력이 아닐까 하는 생각이 든다.&lt;/p&gt;
&lt;h2&gt;Reference&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;WakeOnLAN - Wireshark Wiki&quot;
image: &quot;https://www.wireshark.org/assets/img/wireshark-logo.png&quot;
description: &quot;WakeOnLAN is the protocol name given to the so-called Magic Packet technology, developed by AMD and Hewlett Packard for remotely waking up a remote host that may have been automatically powered-down because of its power management features. Although power management allows companies and individuals to cut power usage costs, it presents a problem for IT departments especially in being able to quickly and efficiently remotely manage PC’s, especially during off-hours operation when those PC’s are most likely to be in a suspended or standby state, assuming power management features are enabled.&quot;
url: &quot;https://wiki.wireshark.org/WakeOnLAN&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;Wake-on-LAN - Wikipedia&quot;
image: &quot;https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Wake_on_LAN_connector.JPG/1200px-Wake_on_LAN_connector.JPG&quot;
description: &quot;Wake-on-LAN (WoL)[a] is an Ethernet or Token Ring computer networking standard that allows a computer to be turned on or awakened from sleep mode by a network message. The message is usually sent to the target computer by a program executed on a device connected to the same local area network (LAN). It is also possible to initiate the message from another network by using subnet directed broadcasts or a WoL gateway service. It is based upon AMD’s Magic Packet Technology, which was co-developed by AMD and Hewlett-Packard, following its proposal as a standard in 1995. The standard saw quick adoption thereafter through IBM, Intel and others.&quot;
url: &quot;https://en.wikipedia.org/wiki/Wake-on-LAN&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;title: &quot;20213.pdf&quot;
image: &quot;https://t3.gstatic.com/faviconV2?client=SOCIAL&amp;amp;type=FAVICON&amp;amp;fallback_opts=TYPE,SIZE,URL&amp;amp;url=https://www.amd.com/content/dam/amd/en/documents/archived-tech-docs/white-papers/20213.pdf&amp;amp;size=128&quot;
description: &quot;Publication# 20213&quot;
url: &quot;https://www.amd.com/content/dam/amd/en/documents/archived-tech-docs/white-papers/20213.pdf&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><category>WOL</category><category>WakeOnLan</category><category>MagicPacket</category><category>Network</category><category>Ethernet</category><category>Hardware</category><author>Jinmu Go</author></item><item><title>Zotero WebDAV 서버를 Coolify에 배포해 첨부파일 동기화하기</title><link>https://jinmugo.com/posts/zotero-webdav-coolify/</link><guid isPermaLink="true">https://jinmugo.com/posts/zotero-webdav-coolify/</guid><description>Zotero 개인 라이브러리의 PDF 첨부파일을 동기화할 WebDAV 서버를 Coolify에 배포하고, HTTPS·영구 저장소·백업까지 안전하게 설정하는 방법.</description><pubDate>Tue, 25 Mar 2025 04:46:34 GMT</pubDate><content:encoded>&lt;p&gt;Zotero에서 직접 운영하는 WebDAV 서버를 쓰려면 &lt;strong&gt;WebDAV 컨테이너에 영구 저장소와 인증 정보를 연결하고, HTTPS 도메인으로 공개한 뒤 Zotero에서 ‘서버 검증’을 실행&lt;/strong&gt;하면 됩니다. 저는 남는 OCI 인스턴스의 Coolify에 &lt;a href=&quot;https://github.com/hacdias/webdav&quot;&gt;hacdias/webdav&lt;/a&gt;를 배포했습니다.&lt;/p&gt;
&lt;p&gt;먼저 범위를 정확히 짚고 가겠습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;동기화 대상&lt;/th&gt;
&lt;th&gt;담당 서비스&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;서지 항목, 컬렉션, 태그, 노트&lt;/td&gt;
&lt;td&gt;Zotero 데이터 동기화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개인 라이브러리의 PDF·이미지 등 첨부파일&lt;/td&gt;
&lt;td&gt;이 글의 WebDAV 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;그룹 라이브러리 첨부파일&lt;/td&gt;
&lt;td&gt;Zotero Storage만 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;WebDAV가 Zotero 계정 전체를 대체하는 것은 아닙니다. 공식 문서 기준으로 WebDAV는 &lt;strong&gt;개인 라이브러리의 첨부파일 동기화에만&lt;/strong&gt; 쓰이며, 그룹 라이브러리에는 사용할 수 없습니다.&lt;/p&gt;
&lt;h2&gt;왜 직접 운영했나&lt;/h2&gt;
&lt;p&gt;저는 PDF를 Zotero에서 읽고 하이라이트와 메모를 남긴 뒤, 아이패드와 맥북에서도 같은 첨부파일을 이어서 보고 싶었습니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Zotero Storage가 가장 간단하고 호환성도 좋지만, 무료 첨부파일 용량을 넘으면 유료 요금제가 필요합니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;이미 운영 중인 서버가 있었기 때문에 WebDAV를 선택했습니다. 새 VPS를 이 용도로만 결제해야 한다면 서버 비용, 업데이트, 백업 시간을 포함해 Zotero Storage와 먼저 비교하는 편이 낫습니다.&lt;/p&gt;
&lt;h2&gt;구성&lt;/h2&gt;
&lt;p&gt;요청 경로는 단순합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Zotero → HTTPS 도메인 → Coolify 리버스 프록시 → webdav:6065 → /data
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Coolify가 TLS 인증서와 외부 도메인을 담당합니다.&lt;/li&gt;
&lt;li&gt;WebDAV 컨테이너는 내부 &lt;code&gt;6065&lt;/code&gt; 포트에서 실행합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/data&lt;/code&gt;는 재배포 뒤에도 남는 볼륨에 저장합니다.&lt;/li&gt;
&lt;li&gt;사용자명과 비밀번호는 Coolify 환경변수로 주입합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. WebDAV 설정 파일 만들기&lt;/h2&gt;
&lt;p&gt;전체 예시 설정을 복사할 필요는 없습니다. Zotero에 필요한 최소 설정은 다음과 같습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;address: 0.0.0.0
port: 6065
tls: false
behindProxy: true
directory: /data
permissions: CRUD

users:
  - username: &quot;{env}ENV_USERNAME&quot;
    password: &quot;{env}ENV_PASSWORD&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;tls: false&lt;/code&gt;인 이유는 TLS 종료를 Coolify가 맡기기 때문입니다. 컨테이너를 인터넷에 포트로 직접 노출한다면 이 구성을 그대로 사용하면 안 됩니다. Zotero 로그인 정보는 HTTP Basic 인증으로 전달되므로 외부 접속은 반드시 HTTPS여야 합니다.&lt;/p&gt;
&lt;p&gt;CORS 설정은 넣지 않았습니다. Zotero 데스크톱·모바일 앱은 브라우저에서 다른 출처로 &lt;code&gt;fetch&lt;/code&gt;하는 웹 앱이 아니므로 이 구성에 CORS 허용 목록은 필요하지 않습니다.&lt;/p&gt;
&lt;h2&gt;2. 버전을 고정한 컨테이너 준비하기&lt;/h2&gt;
&lt;p&gt;Coolify에서 Dockerfile 애플리케이션을 만들고 다음 두 줄을 입력합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;FROM ghcr.io/hacdias/webdav:v5.15.0
ENTRYPOINT [&quot;webdav&quot;, &quot;-c&quot;, &quot;/config.yml&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;latest&lt;/code&gt; 대신 제가 확인한 버전을 고정했습니다. 자동으로 새 이미지를 받아 예기치 않게 동작이 바뀌는 일을 피하고, 업데이트는 릴리스 노트를 확인한 뒤 태그를 바꾸는 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;애플리케이션의 노출 포트는 &lt;code&gt;6065&lt;/code&gt;로 설정하고 도메인을 연결합니다. WebDAV 프로세스는 모든 인터페이스(&lt;code&gt;0.0.0.0&lt;/code&gt;)에서 들어오는 연결을 받아야 Coolify 프록시가 접근할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;3. 설정과 데이터를 영구 저장하기&lt;/h2&gt;
&lt;p&gt;Coolify의 &lt;strong&gt;Persistent Storage&lt;/strong&gt;에서 두 항목을 만듭니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;설정 파일: File Mount로 위 YAML을 &lt;code&gt;/config.yml&lt;/code&gt;에 읽기 전용으로 연결&lt;/li&gt;
&lt;li&gt;첨부파일: Volume Mount를 &lt;code&gt;/data&lt;/code&gt;에 연결&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;컨테이너 파일 시스템은 재배포할 때 바뀔 수 있습니다. &lt;code&gt;/data&lt;/code&gt;를 영구 볼륨에 연결하지 않으면 첨부파일을 잃을 수 있습니다.&lt;/p&gt;
&lt;h2&gt;4. 인증 정보와 HTTPS 도메인 설정하기&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Environment Variables&lt;/strong&gt;에 다음 두 값을 추가합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ENV_USERNAME&lt;/code&gt;: Zotero에 입력할 사용자명&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ENV_PASSWORD&lt;/code&gt;: 길고 고유한 비밀번호&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;비밀번호를 Dockerfile이나 저장소에 직접 적지 않습니다. 배포 후에는 Coolify가 발급한 HTTPS 도메인으로 접속되는지 확인하고, 외부에 &lt;code&gt;6065&lt;/code&gt; 포트를 별도로 열어두지 않습니다.&lt;/p&gt;
&lt;h2&gt;5. Zotero에서 WebDAV 연결하기&lt;/h2&gt;
&lt;p&gt;Zotero의 &lt;strong&gt;설정 → 동기화 → 파일 동기화&lt;/strong&gt;로 이동합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;‘내 라이브러리 첨부파일 동기화 도구’를 &lt;strong&gt;WebDAV&lt;/strong&gt;로 변경합니다.&lt;/li&gt;
&lt;li&gt;프로토콜은 &lt;code&gt;https&lt;/code&gt;를 선택합니다.&lt;/li&gt;
&lt;li&gt;URL에는 도메인만 입력합니다. Zotero가 끝에 &lt;code&gt;/zotero&lt;/code&gt;를 자동으로 붙입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ENV_USERNAME&lt;/code&gt;, &lt;code&gt;ENV_PASSWORD&lt;/code&gt; 값을 입력합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;서버 검증&lt;/strong&gt;을 눌러 성공하는지 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;검증에 성공하면 동기화 버튼을 눌러 작은 PDF 하나부터 올려봅니다. 다른 기기에서 같은 Zotero 계정으로 로그인한 뒤 첨부파일이 열리는지까지 확인해야 설정이 끝난 것입니다.&lt;/p&gt;
&lt;h2&gt;운영 전에 꼭 확인할 것&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;백업:&lt;/strong&gt; Coolify의 영구 볼륨은 백업이 아닙니다. &lt;code&gt;/data&lt;/code&gt;를 다른 서버나 객체 저장소에 주기적으로 복사하고 실제 복원도 시험합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;업데이트:&lt;/strong&gt; 이미지 태그를 고정하고, 새 버전은 백업 후 수동으로 올립니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS:&lt;/strong&gt; 인증 정보와 파일을 보호하려면 외부 URL은 HTTPS만 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;용량:&lt;/strong&gt; 서버 디스크 사용량과 백업 보존 기간을 모니터링합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;장애 범위:&lt;/strong&gt; 자체 WebDAV 장애는 Zotero가 지원해 주지 않습니다. 문제가 생기면 먼저 서버 로그, 저장소 권한, 인증서, &lt;code&gt;/zotero&lt;/code&gt; 경로를 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;직접 운영은 기존 서버 자원을 활용할 때 꽤 만족스럽습니다. 다만 “컨테이너가 실행된다”와 “첨부파일이 안전하다”는 다른 문제였습니다. 영구 저장소, 오프사이트 백업, 복원 시험까지 갖춰야 비로소 Zotero Storage의 대안이라고 부를 수 있습니다.&lt;/p&gt;
&lt;h2&gt;참고 자료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.zotero.org/support/sync&quot;&gt;Zotero 공식 동기화 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.zotero.org/support/preferences/sync&quot;&gt;Zotero 동기화 설정 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hacdias/webdav&quot;&gt;hacdias/webdav 저장소와 설정 예시&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/hacdias/webdav/releases/tag/v5.15.0&quot;&gt;hacdias/webdav v5.15.0 릴리스&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://coolify.io/docs/applications/build-packs/dockerfile&quot;&gt;Coolify Dockerfile 배포 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://coolify.io/docs/knowledge-base/persistent-storage&quot;&gt;Coolify 영구 저장소 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>zotero</category><category>coolify</category><category>webdav</category><author>Jinmu Go</author></item><item><title>Week 21 - 첫 시작.</title><link>https://jinmugo.com/weekly/2026-W21/</link><guid isPermaLink="true">https://jinmugo.com/weekly/2026-W21/</guid><description>운동과 자격증 준비, 사이드 프로젝트 Cinch, 다시 시작한 독서와 미국 출국 준비를 돌아본 2026년 21주 차 기록.</description><pubDate>Sun, 24 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;운동 기록&lt;/h2&gt;
&lt;p&gt;최근 러닝 기록&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;![[2026-W20-1779267190791.webp]]
![[2026-W20-1779267207541.webp]]
![[2026-W20-1779267306603.webp]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;작년에 이어서 올해 춘천마라톤도 나갈 예정이다. 올해는 Sub-4를 목표로 하고 있다. 아직 제대로 연습을 하고 있진 않지만... 그냥 현상 유지정도로 일주일에 적어도 세번은 뛰고 있다.
그것마저도 간신히 하고 있는 터라. 쉬이 5km를 넘지 않는다. 목표와 행동이 조금 어긋나 있지만, 아무튼 일주일에 세번은 꾸준히 하고 있다는 게 어디인가.&lt;/p&gt;
&lt;h2&gt;허간민&lt;/h2&gt;
&lt;p&gt;최근들어 가장 행복한 일중 하나를 꼽으라 하면 러닝하면서 허간민을 듣는 것이다. 허간민은
&lt;strong&gt;허&lt;/strong&gt;키 시바세키, 김&lt;strong&gt;간&lt;/strong&gt;지, 김&lt;strong&gt;민&lt;/strong&gt;경의 글자를 따와서 만든 줄임말이다.
머니그라피라는 토스 채널의 &lt;code&gt;&amp;lt;B주류 초대석&amp;gt;&lt;/code&gt; 코너가 있는데 이 세명이 나와서 영화에 대해 얘기하는게 그 시작이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Rwx2EGl7i2U&quot;&gt;심통난 씨네필 셋과 함께하는 명작 영화 월드컵 | B주류초대석 허키 시바세키&lt;/a&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;지난 9일에는 리눅스마스터 1급 2차 시험을 보았다. 작년에 보았을 때 떨어져서 이번에 심기일전하여 다시 도전하였다. 가채점으로는 합격인데 그래도 확실하진 않으니 29일 결과 발표까지는 어느정도 불안하다. 최근 취업을 해야겠다고 생각하고 이것저것 자격증을 따고 있는데 조금 불안한 마음 때문에 따는 것도 없지않아있다. 그래도 공부하고 어떤 마일스톤을 달성하는 기분이라 게임하는 느낌으로 재밌게 따고 있다. 이번주에는 정처기 필기를 응시하고 왔다. 필기는 바로 점수가 뜨더라. 실기 준비해야지.&lt;/p&gt;
&lt;h3&gt;Cinch&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cinchcli.com&quot;&gt;Cinch&lt;/a&gt; 라는 사이드 프로젝트를 진행하고 있다. 내가 진행한 프로젝트 중에서 처음으로 수익을 내기 위해 진행하고 있는 프로젝트다. 적은돈이라도 돈을 버는 경험을 하고 싶어서 시작했다. 물론 요즘 AI때문에 SaaS가 죽었다(혹은 죽을 것이다)라는 말들이 많지만, 그래도 지금 내가 할 수 있는걸 해봐야겠다는 생각이 들었다.&lt;/p&gt;
&lt;p&gt;Cinch는 클립보드 서비스 이다. 아직 많은 피봇을 하는 중이라 명확히 정의할 수는 없지만. 오늘날 쓰기 편한 클립보드 서비스를 만드는게 목표다. 처음에는 remote clipboard tool 로 시작했는데, 만들다 보니 욕심이 생긴다. 하지만 정말 사람들이 쓰고 싶게, 쓰기 쉽게 만드는 건 너무너무 어렵다. 혼자 서비스를 만들다 보면 개발자와 소비자의 페르소나를 계속해서 왔다갔다 하는 기분이다. 그게 참 힘들다.&lt;br /&gt;
그리고 사용자가 피드백을 준다는게 얼마나 소중한지 최근들어 계속해서 느끼고 있다. 별점 1점도 참 소중하다.&lt;/p&gt;
&lt;h2&gt;독서.&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;#heoganmin&quot;&gt;허간민&lt;/a&gt;의 영향(정확히는 김민경 편집자님의 영향)으로 미뤄왔던 독서를 다시 시작했다. 컨텐츠들이 독서와 문화 쪽에 집중이 되어서 그런지 확실히 가만히 편집자님의 이야기를 듣고 있으면 스멀스멀 책을 읽고 싶어진다. 최근에는 작은 서점에서 진행한 &lt;a href=&quot;https://podcasts.apple.com/us/podcast/1-27-%ED%99%94-%ED%8E%B8%EC%A7%91%EC%9E%90%EC%9D%98-%EB%B0%A9-%EB%B2%84%EC%A7%80%EB%8B%88%EC%95%84-%EC%9A%B8%ED%94%84-%EB%8C%88%EB%9F%AC%EC%9B%A8%EC%9D%B4-%EB%B6%80%EC%9D%B8-with-%EA%B9%80%EB%AF%BC%EA%B2%BD-%ED%8E%B8%EC%A7%91%EC%9E%90-%EC%9C%A0%EC%83%81%ED%9B%88-%ED%8E%B8%EC%A7%91%EC%9E%90/id1811246527?i=1000746927301&quot;&gt;팟캐스트&lt;/a&gt;에서 버지니아 울프에 대해 영업 당해 그녀의 책을 두권 구매했다. &lt;code&gt;&amp;lt;디 에센셜 버지니아 울프&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;댈러웨이 부인&amp;gt;&lt;/code&gt;. 어제 부터 읽기 시작했는데, 재미있다. 역시 나는 에세이 보다는 소설이 좋다. 그리고 소설은 무언가 내가 살지 않았던 삶을 체험하고 다른 사람의 머릿속에 들어갔다가 나오는 기분(?)이라 좋다. 내가 경험해보지 못한 것들을 아는게.&lt;/p&gt;
&lt;p&gt;삶에는 관성이 있다. 이렇게 책을 읽으니 이제는 쓰고 싶어진다. 정처없이 유튜브를 떠돌아 다니다 김민경 편집자를 만나고... 허간민을 보고.... 독서를 시작하고... 블로그에 글을 쓰고..... ㅋㅋㅋㅋㅋ
나도 참 흘러가는 대로 산다. 그래도 흘러가는게 어디랴.&lt;/p&gt;
&lt;h2&gt;미국&lt;/h2&gt;
&lt;p&gt;올해 초에 두달간 미국을 다녀왔다. 보고 느낀게 많아 블로그에 정리하려 하는데 도무지 쓸 여력이 없어서 미루고 미루다 올해 상반기가 다 지나갔다. 하반기에는 꼭 적어봐야지. 나를 위해.&lt;/p&gt;
&lt;h2&gt;시작&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tw93&quot;&gt;좋아하는 개발자&lt;/a&gt;가 일주일 회고를 하는걸 보고 나도 따라하고 싶어 시작했다. 꾸준히 올릴 수 있도록 최대한 노력 해볼 예정이다.&lt;/p&gt;
</content:encoded><category>weekly</category><author>Jinmu Go</author></item><item><title>Week 22 - 식집사</title><link>https://jinmugo.com/weekly/2026-W22/</link><guid isPermaLink="true">https://jinmugo.com/weekly/2026-W22/</guid><description>10km 러닝과 수영, 빠르게 자라는 한련화, CKA 공부와 LeetCode, Hermes Agent 기반 개인 비서 환경을 기록했다.</description><pubDate>Wed, 27 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;운동 기록&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;![[2026-W22-1780229056414.webp]]
![[2026-W22-1780229056415.webp]]
![[2026-W22-1780229056416.webp]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이번주는 10km 한번 뛰고 다리에 좀 무리가 간 것 같아서 이후에는 수영을 했다. 요즘 날씨가 뛰기에 너무 좋아서 10km뛸때 행복했다. 역시 나는 달리는게 좋다.&lt;/p&gt;
&lt;p&gt;처음 수영을 배웠을 때는 계속해서 수영해도 재밌었는데, 요새는 수영이 그렇게 재밌지는 않다. 그냥 날이 더울 때 하기 좋은 운동 정도다. 그래도 장점이 너무 많은 운동이어서 즐겨 하고 있다. (관절에 비교적 무리가 덜 가는 것, 씻고 들어갔다가 나올 수 있는 것, 여름에 너무 더울 때 시원하게 운동할 수 있는 것 등등....)&lt;/p&gt;
&lt;h2&gt;한련화&lt;/h2&gt;
&lt;p&gt;인생 처음으로 식물을 기르고 있다. 이름은 &lt;a href=&quot;https://www.google.com/search?q=%ED%95%9C%EB%A0%A8%ED%99%94&amp;amp;sourceid=chrome&amp;amp;ie=UTF-8&quot;&gt;한련화&lt;/a&gt; 식물원에 갔을 때 데려왔다. 이친구를 고른건 다른게 아니라 주황색 꽃잎이 예쁘다. 나는 유난히 주황색 꽃을 좋아한다.&lt;/p&gt;
&lt;p&gt;그런데 이친구 성장속도가 남다르다. 햇빛이 잘들긴하더라도 이렇게 식물이 하루가 다르게 자라는 걸 처음본다. 다른 꽃이나 식물도 이런걸까. 키우는 맛이 나는 친구다.  너무 잘 커서 화분을 더 큰걸로 갈아줘야하나 고민하고 있다.&lt;/p&gt;
&lt;p&gt;꽃잎도 먹을 수 있다고는 하는데... 음... 왜인지 먹고 싶진 않다. 맛이 있을 것 같지도 않고.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;![[2026-W22-1780232300734.webp]]
![[2026-W22-1780232300728.webp]]
![[2026-W22-1780232774070.webp]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CKA&lt;/h2&gt;
&lt;p&gt;작년 사이버 먼데이 때 사놓은 CKA와  CKAD의 공부를 이번에 시작했다. 총 세달을 잡고 취득해보려 한다. CKA 2달. CKAD 1달.&lt;/p&gt;
&lt;p&gt;2주 정도는 유명한 cka 강의를 들을 예정이고 이후로는 실습해보면서 준비하려한다. 꾸준히. 한번에 모든 지식을 얻으려하지않고 천천히 지식을 습득할 수 있도록. 나는 욕심이 많기 때문에 이렇게 의식적으로 상기 해주어야지 천천히 지식을 소화할 수 있다.&lt;/p&gt;
&lt;h2&gt;LeetCode&lt;/h2&gt;
&lt;p&gt;최근 알고리즘을 다시 시작했다. AI를 이용한 vibe coding이 주류가 되면서 내가 직접 코드를 치는 일이 현저히 줄었는데, 잊고 있었던 직접 코드를 치는 기쁨을 다시 느끼고 있다. 그리고 왜인지 아침에 알고리즘을 풀고 시작하면 머리를 조금 더 잘 쓰고 있다는 기분이 든다. 뇌가 워밍업이 되는 느낌.&lt;/p&gt;
&lt;p&gt;하루에 하나씩은 풀려고 하고 있고 여유가 된다면 weekly contest에도 참여하려 한다.&lt;/p&gt;
&lt;h2&gt;hermes agent&lt;/h2&gt;
&lt;p&gt;hermes agent와 obsidian , syncthing을 이용해서 나만의 개인 비서 인프라? 를 구축했다. 무엇보다도 syncthing을 이용해서 원격의 hermes agent와 서로 수정한 markdown을 보면서 논의 할 수 있다는게 좋다. 이렇게 설정한 것도 한번 블로그에 정리해봐야겠다.&lt;/p&gt;
</content:encoded><category>weekly</category><author>Jinmu Go</author></item><item><title>Week 23 - 난춘</title><link>https://jinmugo.com/weekly/2026-W23/</link><guid isPermaLink="true">https://jinmugo.com/weekly/2026-W23/</guid><description>배탈로 쉬어간 주말, 《기묘한 이야기》와 구글 이스터에그, 늦봄의 혼란 속에서도 방향을 찾으려는 마음을 기록했다.</description><pubDate>Sun, 07 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;운동 기록&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;![[2026-W23-1780834295813.webp]]
![[2026-W23-1780834295815.webp]]
![[2026-W23-1780834295814.webp]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;6월 치고는 날씨가 선선했다. 작년 이맘때쯤에는 더워서 쉽게 밖으로 안나갔던 것 같은데. 이번주 금요일부터 갑자기 배탈이 나서 토요일이랑 일요일을 쉽게 보냈다. 처음 발병한 토요일은 완전 페인 처럼 보내고. 일요일은 그나마 움직일 수 있어서 러닝을 다녀왔다.&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;p&gt;그리고 방금 알아냈는데 구글에 기묘한 이야기를 치면 아래에 주사위가 나온다. 그리고 이걸 누르면 인터넷 검색 창 자체가 뒤집혀져(upside down)버린다. 재미있는 구글의 이스터에그.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;![[2026-W23-1780836624287.webp]]
![[2026-W23-1780836644299.webp]]
![[2026-W23-1780836651877.webp]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;난춘&lt;/h2&gt;
&lt;p&gt;봄은 지난 것 같지만 아직 봄의 끝자락 같기에 이런 제목을 붙여보았다. 정말 어지러운 요즘이다. 이제는 정말 앞으로 어떻게 흘러갈지 가늠하지 못하겠다. 이전에도 뭐 다를바 없지만. 지금은 더 갈피를 못잡겠다. 내가 어디로 흘러갈지 내가 서있는 곳이 어디로 흘러갈지.&lt;/p&gt;
&lt;p&gt;그래도 어디론가 나아가야지 방향을 잡고!&lt;/p&gt;
</content:encoded><category>weekly</category><author>Jinmu Go</author></item></channel></rss>