Deno v2.6.4 just shipped with a fix for Intel Macs and a big performance improvement to `node:http` module.
洪 民憙 (Hong Minhee)
@hongminhee@hackers.pub · 1010 following · 719 followers
Hi, I'm who's behind Fedify, Hollo, BotKit, and this website, Hackers' Pub! My main account is at
@hongminhee洪 民憙 (Hong Minhee)
.
Fedify, Hollo, BotKit, 그리고 보고 계신 이 사이트 Hackers' Pub을 만들고 있습니다. 제 메인 계정은:
@hongminhee洪 民憙 (Hong Minhee)
.
Fedify、Hollo、BotKit、そしてこのサイト、Hackers' Pubを作っています。私のメインアカウントは「
@hongminhee洪 民憙 (Hong Minhee)
」に。
Website
- hongminhee.org
GitHub
- @dahlia
Hollo
- @hongminhee@hollo.social
DEV
- @hongminhee
velog
- @hongminhee
Qiita
- @hongminhee
Zenn
- @hongminhee
Matrix
- @hongminhee:matrix.org
X
- @hongminhee
프로토타입은 만들었고, 이제 각 언어별로 resolver?를 만들면 된다. JSON, JS 순으로 해야지.
소프트웨어 엔지니어 채용중입니다. https://careers.linecorp.com/ko/jobs/2961/ Rust(도) 하는 팀입니다.
훌륭한 프로그래머인 @perlmint 님와 함께 일할 수 있습니다
I finally gave in and wrote my own markdownlint rules to enforce my peculiar and stubborn Markdown style. Probably no one else will ever need these, but I've published them as open source anyway.
@bglbgl gwyng 오… 좋은 아이디어 같아요! MCP 서버로도 동작하면 좋을 것 같네요. 당장 저장소 파주세요.
@hongminhee洪 民憙 (Hong Minhee) https://github.com/bglgwyng/lat 일단 아이디어 기록 용으로 팠습니다. vertana로 영문 README를 만들어 봐야겠네요.
LLM을 위한 cat, lat을 만들어볼까 생각이 들었다. 어떤 파일을 열던지 간에, 토큰 아끼고 LLM이 쉽게 읽도록 적당히 알아서 바꿔서 보여주는 것이다. 그리고 CLAUDE.md 같은거에 cat 대신에 쓰라고 하는거지.
처음엔 JSON 파일을 minify해서 토큰 아끼는 정도를 생각했는데, 막상 클로드한테 물어보니 자기도 들여쓰기가 있어야 읽기 편하다고 한다. 응? 그래서 쓸모있는 접근을 물어봤더니, 코드를 읽을때 앞에 함수 시그니쳐/클래스 정의 등의 요약을 달아주면 좋겠다고 한다.
쓸모있게 만드려면 좀더 고민해야할듯..
LLM에서 마크다운이 널리 쓰이게 되면서 안 보고 싶어도 볼 수 밖에 없게 된 흔한 꼬라지로 그림에서 보는 것처럼 마크다운 강조 표시(**)가 그대로 노출되어 버리는 광경이 있다. 이 문제는 CommonMark의 고질적인 문제로, 한 10년 전쯤에 보고한 적도 있는데 지금까지 어떤 해결책도 제시되지 않은 채로 방치되어 있다.
문제의 상세는 이러하다. CommonMark는 마크다운을 표준화하는 과정에서 파싱의 복잡도를 제한하기 위해 연속된 구분자(delimiter run)라는 개념을 넣었는데, 연속된 구분자는 어느 방향에 있느냐에 따라서 왼편(left-flanking)과 오른편(right-flanking)이라는 속성을 가질 수 있다(왼편이자 오른편일 수도 있고, 둘 다 아닐 수도 있다). 이 규칙에 따르면 **는 왼편의 연속된 구분자로부터 시작해서 오른편의 연속된 구분자로 끝나야만 한다. 여기서 중요한 건 왼편인지 오른편인지를 판단하는 데 외부 맥락이 전혀 안 들어가고 주변의 몇 글자만 보고 바로 결정된다는 것인데, 이를테면 왼편의 연속된 구분자는 **<보통 글자> 꼴이거나 <공백>**<기호> 또는 <기호>**<기호> 꼴이어야 한다. ("보통 글자"란 공백이나 기호가 아닌 글자를 가리킨다.) 첫번째 꼴은 아무래도 **마크다운**은 같이 낱말 안에 끼어 들어가 있는 연속된 구분자를 허용하기 위한 것이고, 두번째/세번째 꼴은 이 **"마크다운"** 형식은 같이 기호 앞에 붙어 있는 연속된 구분자를 제한적으로 허용하기 위한 것이라 해석할 수 있겠다. 오른편도 방향만 다르고 똑같은 규칙을 가지는데, 이 규칙으로 **마크다운(Markdown)**은을 해석해 보면 뒷쪽 **의 앞에는 기호가 들어 있으므로 뒤에는 공백이나 기호가 나와야 하지만 보통 글자가 나왔으므로 오른편이 아니라고 해석되어 강조의 끝으로 처리되지 않는 것이다.
CommonMark 명세에서도 설명되어 있지만, 이 규칙의 원 의도는 **이런 **식으로** 중첩되어** 강조된 문법을 허용하기 위한 것이다. 강조를 한답시고 **이런 ** 식으로 공백을 강조 문법 안쪽에 끼워 넣는 일이 일반적으로는 없으므로, 이런 상황에서 공백에 인접한 강조 문법은 항상 특정 방향에만 올 수 있다고 선언하는 것으로 모호함을 해소하는 것이다. 허나 CJK 환경에서는 공백이 아예 없거나 공백이 있어도 한국어처럼 낱말 안에서 기호를 쓰는 경우가 드물지 않기 때문에, 이런 식으로 어느 연속된 구분자가 왼편인지 오른편인지 추론하는 데 한계가 있다는 것이다. 단순히 <보통 문자>**<기호>도 왼편으로 해석하는 식으로 해서 **마크다운(Markdown)**은 같은 걸 허용한다 하더라도, このような**[状況](...)**は 이런 상황은 어쩔 것인가? 내가 느끼기에는 중첩되어 강조된 문법의 효용은 제한적인 반면 이로 인해 생기는 CJK 환경에서의 불편함은 명확하다. 그리고 LLM은 CommonMark의 설계 의도 따위는 고려하지 않고 실제 사람들이 사용할 법한 식으로 마크다운을 쓰기 때문에, 사람들이 막연하게 가지고만 있던 이런 불편함이 그대로 표면화되어 버린 것이고 말이다.
As Markdown has become the standard for LLM outputs, we are now forced to witness a common and unsightly mess where Markdown emphasis markers (**) remain unrendered and exposed, as seen in the image. This is a chronic issue with the CommonMark specification---one that I once reported about ten years ago---but it has been left neglected without any solution to this day.
The technical details of the problem are as follows: In an effort to limit parsing complexity during the standardization process, CommonMark introduced the concept of "delimiter runs." These runs are assigned properties of being "left-flanking" or "right-flanking" (or both, or neither) depending on their position. According to these rules, a bolded segment must start with a left-flanking delimiter run and end with a right-flanking one. The crucial point is that whether a run is left- or right-flanking is determined solely by the immediate surrounding characters, without any consideration of the broader context. For instance, a left-flanking delimiter must be in the form of **<ordinary character>, <whitespace>**<punctuation>, or <punctuation>**<punctuation>. (Here, "ordinary character" refers to any character that is not whitespace or punctuation.) The first case is presumably intended to allow markers embedded within a word, like **마크다운**은, while the latter cases are meant to provide limited support for markers placed before punctuation, such as in 이 **"마크다운"** 형식은. The rules for right-flanking are identical, just in the opposite direction.
However, when you try to parse a string like **마크다운(Markdown)**은 using these rules, it fails because the closing ** is preceded by punctuation (a parenthesis) and it must be followed by whitespace or another punctuation mark to be considered right-flanking. Since it is followed by an ordinary letter (은), it is not recognized as right-flanking and thus fails to close the emphasis.
As explained in the CommonMark spec, the original intent of this rule was to support nested emphasis, like **this **way** of nesting**. Since users typically don't insert spaces inside emphasis markers (e.g., **word **), the spec attempts to resolve ambiguity by declaring that markers adjacent to whitespace can only function in a specific direction. However, in CJK (Chinese, Japanese, Korean) environments, either spaces are completly absent or (as in Korean) punctuations are commonly used within a word. Consequently, there are clear limits to inferring whether a delimiter is left or right-flanking based on these rules. Even if we were to allow <ordinary character>**<punctuation> to be interpreted as left-flanking to accommodate cases like **마크다운(Markdown)**은, how would we handle something like このような**[状況](...)は**?
In my view, the utility of nested emphasis is marginal at best, while the frustration it causes in CJK environments is significant. Furthermore, because LLMs generate Markdown based on how people would actually use it---rather than strictly following the design intent of CommonMark---this latent inconvenience that users have long felt is now being brought directly to the surface.
Zod가 lazy validation을 지원안하길래 대안을 찾아봤는데, 잘 알려진것중엔 Valibot의 v.lazy 뿐인것 같다
LLM에서 마크다운이 널리 쓰이게 되면서 안 보고 싶어도 볼 수 밖에 없게 된 흔한 꼬라지로 그림에서 보는 것처럼 마크다운 강조 표시(**)가 그대로 노출되어 버리는 광경이 있다. 이 문제는 CommonMark의 고질적인 문제로, 한 10년 전쯤에 보고한 적도 있는데 지금까지 어떤 해결책도 제시되지 않은 채로 방치되어 있다.
문제의 상세는 이러하다. CommonMark는 마크다운을 표준화하는 과정에서 파싱의 복잡도를 제한하기 위해 연속된 구분자(delimiter run)라는 개념을 넣었는데, 연속된 구분자는 어느 방향에 있느냐에 따라서 왼편(left-flanking)과 오른편(right-flanking)이라는 속성을 가질 수 있다(왼편이자 오른편일 수도 있고, 둘 다 아닐 수도 있다). 이 규칙에 따르면 **는 왼편의 연속된 구분자로부터 시작해서 오른편의 연속된 구분자로 끝나야만 한다. 여기서 중요한 건 왼편인지 오른편인지를 판단하는 데 외부 맥락이 전혀 안 들어가고 주변의 몇 글자만 보고 바로 결정된다는 것인데, 이를테면 왼편의 연속된 구분자는 **<보통 글자> 꼴이거나 <공백>**<기호> 또는 <기호>**<기호> 꼴이어야 한다. ("보통 글자"란 공백이나 기호가 아닌 글자를 가리킨다.) 첫번째 꼴은 아무래도 **마크다운**은 같이 낱말 안에 끼어 들어가 있는 연속된 구분자를 허용하기 위한 것이고, 두번째/세번째 꼴은 이 **"마크다운"** 형식은 같이 기호 앞에 붙어 있는 연속된 구분자를 제한적으로 허용하기 위한 것이라 해석할 수 있겠다. 오른편도 방향만 다르고 똑같은 규칙을 가지는데, 이 규칙으로 **마크다운(Markdown)**은을 해석해 보면 뒷쪽 **의 앞에는 기호가 들어 있으므로 뒤에는 공백이나 기호가 나와야 하지만 보통 글자가 나왔으므로 오른편이 아니라고 해석되어 강조의 끝으로 처리되지 않는 것이다.
CommonMark 명세에서도 설명되어 있지만, 이 규칙의 원 의도는 **이런 **식으로** 중첩되어** 강조된 문법을 허용하기 위한 것이다. 강조를 한답시고 **이런 ** 식으로 공백을 강조 문법 안쪽에 끼워 넣는 일이 일반적으로는 없으므로, 이런 상황에서 공백에 인접한 강조 문법은 항상 특정 방향에만 올 수 있다고 선언하는 것으로 모호함을 해소하는 것이다. 허나 CJK 환경에서는 공백이 아예 없거나 공백이 있어도 한국어처럼 낱말 안에서 기호를 쓰는 경우가 드물지 않기 때문에, 이런 식으로 어느 연속된 구분자가 왼편인지 오른편인지 추론하는 데 한계가 있다는 것이다. 단순히 <보통 문자>**<기호>도 왼편으로 해석하는 식으로 해서 **마크다운(Markdown)**은 같은 걸 허용한다 하더라도, このような**[状況](...)**は 이런 상황은 어쩔 것인가? 내가 느끼기에는 중첩되어 강조된 문법의 효용은 제한적인 반면 이로 인해 생기는 CJK 환경에서의 불편함은 명확하다. 그리고 LLM은 CommonMark의 설계 의도 따위는 고려하지 않고 실제 사람들이 사용할 법한 식으로 마크다운을 쓰기 때문에, 사람들이 막연하게 가지고만 있던 이런 불편함이 그대로 표면화되어 버린 것이고 말이다.
@mitsuhikoArmin Ronacher
@glyph
That response sounds like you are admitting that LLMs are just repackaging human work against the will of those humans?
Repackaging someone else's work and putting your name on it is not sharing, it's stealing labour and stealing credit. Pirates don't pretend they made something, but LLM users do pretend this.
I agree 70 years is too long, but LLMs are reducing that to 0 years while also erasing from history the creator's credit.
Open source is where the creators are credited and they have consented to their work being used by others. LLMs are removing credit and doing it without consent.
@FediThingFediThing 🏳️🌈
@glyph LLMs are trained on human-created material in much the same way a person learns by reading books and then acting on what they've learned. They don't directly reproduce that material.
As I mentioned I strongly believe that broad sharing of knowledge is a net benefit to humanity. Questions of credit and attribution are a separate issue and to discuss them meaningfully, you first have to be clear about what you consider reasonable attribution in the first place.
You can take for instance the tankgame and then tell me which part should be attributed and is not, and what you would be attributing it to.
On the "against the will": I want you to use the code I wrote, it's definitely not against my will that LLMs are trained on the code I wrote over the years.
SPF & DKIM records
Permalink: https://wizardzines.com/comics/spf-dkim/
#Optique 0.9.0 is here!
This release brings #async/await support to #CLI parsers. Now you can validate input against external resources—databases, APIs, Git repositories—directly at parse time, with full #TypeScript type safety.
The new @optique/git package showcases this: validate branch names, tags, and commit SHAs against an actual Git repo, complete with shell completion suggestions.
Other highlights:
- Hidden option support for deprecated/internal flags
- Numeric choices in
choice() - Security fix for shell completion scripts
Fully backward compatible—your existing parsers work unchanged.
My last salaried job was at a company that built blockchain technology. No, it wasn't for cryptocurrency. The goal was to use blockchain to create a fully peer-to-peer, decentralized game. I found it a technically interesting goal. I've always been fascinated by decentralized technologies, which is also why I'm drawn to ActivityPub. Another thing that attracted me was the promise that this technology would be implemented as 100% open source. I had always wanted to work on open source full-time, so I accepted the offer.
However, once I started working there, I found myself increasingly disappointed. The organization gradually filled up with so-called “crypto bros,” and the culture shifted toward prioritizing token price over technical achievement. I and a few close colleagues believed that introducing partial centralization to the fully decentralized system—whether to defend the token price or to rush a release—was not a “minor compromise” but a “major corruption.” The rest of the organization didn't see it that way.
One of the most painful things about being in that organization was the fact that the technology I was creating was not only unhelpful to society, but was actually harming the environment and society. At the time, I felt like I was working for a tobacco company—knowing that cigarettes harm people's health, yet turning a blind eye and doing the job anyway.
I'm no fan of cryptocurrency, but I still think blockchain has technically interesting aspects. However, blockchain has already become socially inseparable from cryptocurrency, and even if blockchain is technically interesting, there are very few domains where it's actually useful. Furthermore, the negative environmental impact of blockchain technology is a problem that must be solved for it to be taken seriously. In its current state, when I weigh the harm against the utility, I believe the harm overwhelmingly outweighs it.
Anyway, I have now completely said goodbye to blockchain technology. I feel at ease now that I don't have to live with that guilt anymore. I also came to realize that engineers must consider not only the technical interest of a technology but also its social impact. So for now, I want to focus on ActivityPub. I find it both technically interesting and socially meaningful!
Wrote about designing type-safe sync/async mode support in TypeScript. Making object({ sync: syncParser, async: asyncParser }) automatically infer as async turned out to be trickier than expected.
https://hackers.pub/@hongminhee/2026/typescript-sync-async-type-safety
複数のパーサーを合成するとき、一つでも非同期なら結果も非同期になる——これをTypeScriptの型レベルで表現するのが意外と難しかった。Optiqueでの設計過程を書きました。
클로드 코드 쓰고있으니 더 나은 VCS에 대한 욕심이 커진다. 나는 클로드가 브랜치를 더 자주 쪼개서, 원하는 시점으로의 롤백이 더 편해졌으면 좋겠다.
하나 생각나는 아이디어는 브랜치 명을 hierachial하게 만들어서 가령 fix-bug-1/refactor-class-foo/fix-function-bar 이런식으로, 무슨 일하는지의 맥락을 브랜치명에 나타내는 것이다. 그리고 a/b 브랜치는 a 브랜치의 자식이어야 한다는 제약도 강제한다.
...는 git은 a란 브랜치가 이미 있으면a/b, a/c 같은 브랜치를 못만든다. 이유는 바로... git 브랜치가 이름 디렉토리로 관리되기 때문이다. 뭐 이런;;
근데 솔직히 마크다운이 "너 진짜 **핵심**을 찔렀어"의 형태로 대중화가 될 줄은 몰랐지...
에이전트가 도구를 다루는 방식에 대한 인식을 동료들과 맞추기 위해 자손킴님의 Tool Use 글을 팀에 공유했고 (👍 을 받음)
고등학생 때부터 Vim을 썼으니까, Vim/Neovim을 합치면 거의 15년 가까이 썼던 것 같다. 그러다가 Deno와 TypeScript를 접하면서 Visual Studio Code로 갈아탔는데, 그러고 한 2–3년? Zed가 나와서 Zed를 또 1년 가까이 썼다. (아, VS Code를 쓸 때도 Zed를 쓸 때도 Vim 키 바인딩을 끄지는 못 했다.)
그런데 요즘에는 Claude Code니 OpenCode니 LLM 기반의 코딩 에이전트들을 꽤 열심히 쓰게 되면서 에디터 자체를 잘 안 쓰게 되었다. 심지어 import 한 줄 추가하는 것도 프롬프트로 해결하게 된다. 그래야 LLM한테 맥락이 주어져서 혼선이 없기 때문이다. (내가 말 없이 코드를 고쳐 두면 LLM이 뭔가의 이상 상황으로 받아들이거나, 무심코 원래 코드로 되돌리기도 한다.) 그러다 보니 커밋 직전에 디테일을 손 보거나 코드를 리뷰할 때 빼고는 에디터를 잘 안 켜게 된다. 켜더라도 즉각적으로 열리는 걸 선호하게 되어서, Vim/Neovim이 가장 먼저 손이 가더라.
결국에는 몇 년 동안의 방황을 거쳐 다시 Vim/Neovim으로 돌아오게 되었다는 이야기. 그래서 조만간 먼지가 쌓인 Vim/Neovim 설정도 새해 맞이를 겸해서 한 번 청소를 해야겠다 싶다.
@hongminhee洪 民憙 (Hong Minhee) 저도 비슷한 이유로 Emacs로 회귀했다가 지금은 Zed를 쓰게 되었습니다 😂
모든 연락 다 끊고 (트위터 제외) 쭉 쉬었더니 아주 좋았다
I wrote Zig bindings to quickjs-ng with 96% API coverage (~240 exported C decls) with unit tests, examples, and doc strings on all functions in less than 6 total hours with AI assistance. I never want to hear that AI isn't faster ever again. https://github.com/mitchellh/zig-quickjs-ng
This isn't slop. I worked for those 6 hours.
I was reviewing everything it outputted, updating my AGENTS.md to course correct future work, ensuring the output was idiomatic Zig, writing my own tests on the side to verify its work (while it worked), and more. My work was split across ~40 separate Amp threads (not one mega session, which doesn't work anyways unless you're orchestrating).
I have a ton of experience writing bindings to libraries for various languages, especially Zig. I have never achieved this much coverage in so little time with such high quality (e.g. test coverage). My usual approach is to get bind just-enough of the surface area to do my actual work and move on. This time I thought I'd draw the whole owl, because it's a new world. And I'm very happy with the result.
Anyone with experience writing bindings knows that you do some small surface area, then the rest of the coverage is annoying repetition. That's why I usually stopped. Well, LLMs/agents are really, really good at annoying repetition and pattern matching. So going from 5% API coverage to 95% is... cake.
There is probably some corners that are kind of nasty still, but I've been re-reviewing every line of code manually and there is nothing major. Definitely some areas that can just use a nicer Zig interfaces over the C API, but that's about it.
I plan on writing a longer form blog showcasing my threads, but you can at least see the final AGENTS.md I produced in the linked repo.
I will repeat that I was not sitting back at all during those 6 hours. While agents were working, I was working, just on separate -- but related -- tasks. I know for a fact that I could not have completed this amount of work in 6 hours fully manually (based on the experience that I've written something like 30+ bindings to C libraries in the past decade, probably more).
I wrote Zig bindings to quickjs-ng with 96% API coverage (~240 exported C decls) with unit tests, examples, and doc strings on all functions in less than 6 total hours with AI assistance. I never want to hear that AI isn't faster ever again. https://github.com/mitchellh/zig-quickjs-ng
This isn't slop. I worked for those 6 hours.
I was reviewing everything it outputted, updating my AGENTS.md to course correct future work, ensuring the output was idiomatic Zig, writing my own tests on the side to verify its work (while it worked), and more. My work was split across ~40 separate Amp threads (not one mega session, which doesn't work anyways unless you're orchestrating).
I have a ton of experience writing bindings to libraries for various languages, especially Zig. I have never achieved this much coverage in so little time with such high quality (e.g. test coverage). My usual approach is to get bind just-enough of the surface area to do my actual work and move on. This time I thought I'd draw the whole owl, because it's a new world. And I'm very happy with the result.
Anyone with experience writing bindings knows that you do some small surface area, then the rest of the coverage is annoying repetition. That's why I usually stopped. Well, LLMs/agents are really, really good at annoying repetition and pattern matching. So going from 5% API coverage to 95% is... cake.
There is probably some corners that are kind of nasty still, but I've been re-reviewing every line of code manually and there is nothing major. Definitely some areas that can just use a nicer Zig interfaces over the C API, but that's about it.
I plan on writing a longer form blog showcasing my threads, but you can at least see the final AGENTS.md I produced in the linked repo.
jekyll 블로그에도 심플한 좋아요 기능을 달 수 있을 것인가? https://burgeonlab.com/blog/add-appreciation-buttons-to-hugo-with-iine/ 가 도움이 될 거 같아서 일단 북마크. 계속 jekyll 을 쓸지도 생각 좀 해봐야 되는데.
Heya! I just released XenoAtom.Terminal https://github.com/XenoAtom/XenoAtom.Terminal, a modern replacement for System.Console for .NET CLI/TUI apps. 🎉
It keeps a familiar Console-like feel, but adds the terminal-native stuff System.Console doesn't cover well: ANSI/VT styling + markup, unified async input events (keys/resize/mouse/paste), restore-on-dispose scopes (raw/cbreak, alternate screen, hide cursor…), clipboard, a rich ReadLine editor, & testable backends, built on top of XenoAtom.Ansi ✨
NATが諸悪の根源(過激)
IPv6가 30주년을 맞았지만 여전히 세계를 장악하지 못한 이유
------------------------------
- 1995년 등장한 IPv6 는 32비트에서 128비트로 확장된 주소 체계를 통해 *인터넷 주소 고갈 문제* 를 해결하려 했음
- 그러나 *IPv4와의 비호환성* , *기능적 차별성 부족* , *NAT의 확산* 등으로 인해 전환이 지연됨
- 전문가들은 *배포 비용과 복잡성* , *ROI 부족* , *성능 불일치* 등이 여전히 주요 …
------------------------------
https://news.hada.io/topic?id=25533&utm_source=googlechat&utm_medium=bot&utm_campaign=1834
프로그래밍 언어 문법을 만들때, 비교 연산자에 <, <=, >, >= 등이 있는데, 어차피 좌우 순서만 바꾸면 되니까, >, >= 같은걸 그냥 압수하고 <, <=만 쓰게 한다음에 >, >= 요건 다른 용도로 쓰면 어떨까하는 생각이 듬.
Using Nano Banana Pro, I composited an image to make it look like the cute dinosaur from the Fedify logo was standing in front of the ULB (Université libre de Bruxelles) building in Brussels, where FOSDEM is held.
This time, I tried writing a prompt to draw an illustration of the mascots from the Mastodon, Lemmy, Fedify, Misskey, and Akkoma projects all getting along together.
Using Nano Banana Pro, I composited an image to make it look like the cute dinosaur from the Fedify logo was standing in front of the ULB (Université libre de Bruxelles) building in Brussels, where FOSDEM is held.
미지의 영역을 프로토타이핑할때는 Sonnet으로 충분한듯... Opus 깊생하게 해봤자 토큰만 더먹는다
클로드 맥스 5x 월 16만원... 10만원만 됐어도 눈감고 지르는건데
洪 民憙 (Hong Minhee) shared the below article:
도커로 구축한 랩에서 혼자 실습하며 배우는 네트워크 프로토콜 입문 #5-3 DHCP
자손킴 @jasonkim@hackers.pub
DHCP(Dynamic Host Configuration Protocol)
DHCP는 IP주소, 서브넷 마스크, 기본 게이트웨이와 DNS 서버의 IP 주소 등 네트워크에 접속하기 위해 필요한 설정을 배포하는 프로토콜이다. RFC2131 에서 표준화 되어 있으며, DHCP의 역할, 메시지 형식, 형식을 구성하는 필드의 의미와 처리 흐름등이 상세하게 정의 되어 있다.
책에서는 DHCP를 L7에서 다루고 있는데, 정리하다보니 이게 L7에 있는게 맞나 싶은 생각이 든다.
IP 할당 방식
IP 주소를 단말(NIC)에 할당하는 방법에는 크게 '정적 할당'과 '동적 할당' 두 가지가 있다.
정적 할당
단말에 대해 일일이 수동으로 IP 주소를 설정하는 방식이다. 시스템 관리자가 비어있는 IP 주소를 사용자에게 할당해 준다.
정적 할당은 단말과 IP 주소가 고유하게 매핑되기 때문에 IP 주소 관리가 용이하다. 특정 IP의 이상 징후가 발생하면 어떤 단말이 문제인지 즉시 파악 할 수 있다. 그러나 단말의 수가 많아 지거나 교체가 빈번하면 하나씩 관리하기 어렵다는 문제가 있다.
동적 할당
DHCP를 사용하여 단말에 자동으로 IP 주소를 설정하는 방법이다. 정적 할당은 사용자가 시스템 관리자에게 요청하여 빈 IP 주소를 지급받아 수동 설정 했다면, 동적 할당은 이 모든 과정을 DHCP가 자동으로 처리한다.
DHCP 메시지 형식
DHCP는 UDP/67로 캡슐화된 DHCP 메시지 부분에 설정 정보를 담는다. DHCP 메시지는 여러 가지 필드로 구성되는데, 다음 3가지가 특히 중요하다.
- 할당 클라이언트 IP 주소
- DHCP 서버에서 단말에 배포하는 IP 주소
- 클라이언트 MAC 주소
- 단말의 MAC 주소
- 옵션
- 네트워크 설정에 관한 다양한 정보
옵션은 옵션 코드에 의해 식별되는데, 대표적인 코드는 다음과 같다.
- (1) 서브넷 마스크
- (3) 기본 게이트웨이
- (6) DNS 서버 IP 주소
- (12) 호스트 이름
- (42) NTP 서버의 IP 주소
- (51) IP 주소 임대 시간
- (53) DHCP 메시지 유형
- (54) DHCP 서버 ID
DHCP 처리 흐름
DHCP는 서버와 클라이언트로 구성되어 있다. DHCP 클라이언트가 있는 단말은 초기에는 IP가 할당되지 않은 상태이기 때문에 브로트캐스트를 통해 정보를 주고 받는다.
- DHCP Discover: 클라이언트가 네트워크에 접속하면 DHCP 서버를 찾기 위해 브로드캐스트로 Discover 메시지를 전송한다. 이 시점에서 클라이언트는 아직 IP 주소가 없으므로 출발지 IP는 0.0.0.0으로 설정된다.
- DHCP Offer: DHCP 서버가 Discover 메시지를 수신하면 할당 가능한 IP 주소와 서브넷 마스크, 임대 시간 등의 설정 정보를 담아 Offer 메시지를 유니캐스트로 응답한다. 네트워크에 여러 DHCP 서버가 존재하는 경우 클라이언트는 복수의 Offer를 수신할 수 있다.
- DHCP Request: 클라이언트가 수신한 Offer 중 하나를 선택하여 해당 DHCP 서버에 IP 주소 할당을 정식으로 요청한다. 이 메시지도 브로드캐스트로 전송되며, 선택되지 않은 다른 DHCP 서버들에게 해당 Offer가 거절되었음을 알리는 역할도 한다.
- DHCP ACK: DHCP 서버가 Request를 승인하면 ACK 메시지를 전송하여 IP 주소 할당을 확정한다. 클라이언트는 이 메시지를 수신한 후 비로소 할당받은 IP 주소를 사용할 수 있게 된다. 만약 요청한 IP 주소를 할당할 수 없는 경우에는 DHCP NAK 메시지가 전송된다.
- DHCP Release: 클라이언트가 더 이상 IP 주소를 사용하지 않을 때 서버에 반환을 알리는 메시지이다. 이 메시지를 수신한 서버는 해당 IP 주소를 풀(Pool)에 반환하여 다른 클라이언트가 사용할 수 있도록 한다.
IP 주소 임대와 갱신
DHCP로 할당받은 IP 주소는 영구적인 것이 아니라 임대 시간(Lease Time)이 정해져 있다. 클라이언트는 임대 시간이 만료되기 전에 갱신을 요청해야 하는데, 일반적으로 임대 시간의 절반(50%)이 경과하면 DHCP Request 메시지를 서버에 유니캐스트로 전송하여 갱신을 시도한다. 이 갱신이 실패하면 임대 시간의 7/8(87.5%)가 경과한 시점에 다시 브로드캐스트로 갱신을 시도하며, 그래도 실패하면 임대 만료 시 IP 주소 사용을 중단하고 처음부터 Discover 과정을 다시 수행한다.
네트워크 인프라 자동화의 기반이 되는 DHCP
DHCP는 IP 주소 할당 외에도 옵션 필드를 활용하여 다양한 부가 기능을 제공할 수 있다. 그 중 대표적인 것이 네트워크 부팅(PXE) 지원이다.
- (66) TFTP 서버 이름
- 부트 파일을 제공하는 TFTP 서버의 IP 주소 또는 호스트명
- (67) 부트 파일 이름
- 클라이언트가 다운로드해야 할 네트워크 부트 프로그램의 경로
PXE 부팅
PXE(Preboot Execution Environment)는 로컬 저장 장치 없이 네트워크를 통해 운영체제를 부팅하는 기술이다. PXE를 사용하면 서버의 NIC가 네트워크에서 부팅 이미지를 받아와 자동으로 OS 설치를 진행할 수 있다.
PXE 부팅의 동작 흐름은 다음과 같다.
- 클라이언트가 전원을 켜면 NIC의 PXE 펌웨어가 DHCP Discover 메시지를 브로드캐스트한다.
- DHCP 서버는 IP 주소와 함께 옵션 66(TFTP 서버 주소)과 옵션 67(부트 파일 경로)을 응답한다.
- 클라이언트는 TFTP 프로토콜을 사용하여 지정된 서버에서 부트 파일(예: pxelinux.0, bootx64.efi)을 다운로드한다.
- 다운로드한 부트 로더가 실행되어 OS 설치 또는 부팅이 진행된다.
이처럼 DHCP의 옵션 필드를 활용하면 단순한 IP 할당을 넘어 네트워크 인프라 자동화의 기반을 구축할 수 있다.
洪 民憙 (Hong Minhee) shared the below article:
도커로 구축한 랩에서 혼자 실습하며 배우는 네트워크 프로토콜 입문 #5-4 SSL 오프로드
자손킴 @jasonkim@hackers.pub
다음에 6장이 있지만 총정리를 하는 챕터이기 때문에 실질적인 내용은 여기까지가 끝이다. 책에서는 SSL 오프로드만 다루고 끝나지만 내부망 안에서의 보안도 짧게 정리했다.
TLS(SSL) 오프로드
TLS를 사용하면 보안이 강화되지만 TLS 핸드셰이크와 암복호화 작업에 CPU 자원을 많이 소모하게 된다. 이것을 전용 장비에 맡기면 웹서버는 효율적으로 애플리케이션 로직 처리에만 신경 쓰면 된다. 특히 로드밸런서에서 중앙집중식으로 TLS를 처리하면 웹서버의 부하가 크게 줄어들고 요청을 빠르게 처리할 수 있게 되어 시스템 전반적으로 부하 분산의 효과가 커진다. 특히 관리가 까다로운 공인 인증서를 로드밸런서에서 집중 관리할 수 있어 운영 부담이 크게 줄어든다.
TLS 오프로드 동작방식
-
클라이언트의 HTTPS 요청: 클라이언트가 웹 서비스에 접속하기 위해 HTTPS 요청을 보낸다. 이 시점에서 클라이언트는 TLS 핸드셰이크를 시작한다.
-
방화벽(fw1) 통과: 요청이 방화벽
fw1에 도달한다. 방화벽은 허용된 포트(443)로 들어오는 트래픽인지 확인하고, 패킷 필터링 규칙에 따라 트래픽을 통과시킨다. 이 단계에서 트래픽은 여전히 암호화된 상태이므로 일반적인 방화벽은 패킷의 내용을 검사할 수 없고 IP/포트 기반 필터링만 수행한다. (다만 SSL 인스펙션 기능이 있는 차세대 방화벽(NGFW)은 여기서 복호화 후 검사를 수행하기도 한다.) -
로드밸런서(lb1)에서 TLS 종료: 로드밸런서
lb1이 클라이언트와 TLS 핸드셰이크를 완료한다. 서버 인증서를 클라이언트에게 제시하고 세션 키를 협상한 뒤 암호화된 트래픽을 복호화한다. 이 과정을 SSL/TLS Termination이라고 부른다. -
평문 HTTP로 백엔드 전달: 로드밸런서는 복호화된 요청을 분석하여 라우팅 규칙에 따라 백엔드 서버
sv1또는sv2에 평문 HTTP로 전달한다. 이때 로드밸런서는X-Forwarded-For,X-Forwarded-Proto같은 헤더를 추가하여 원본 클라이언트 정보와 프로토콜 정보를 백엔드에 전달할 수 있다. -
백엔드 서버 처리: 웹서버
sv1또는sv2는 평문 HTTP 요청을 받아 애플리케이션 로직을 처리하고 응답을 생성한다. 암복호화 작업이 없으므로 CPU 자원을 온전히 비즈니스 로직에 사용할 수 있다. -
응답 암호화 및 전송: 백엔드 서버의 HTTP 응답이 로드밸런서
lb1로 돌아오면, 로드밸런서는 이를 TLS로 암호화하여 클라이언트에게 전송한다.
망분리는 만병통치약이 아니다.
TLS 오프로드를 하게 되면 로드밸런서를 통과한 패킷은 평문으로 내부망을 돌아다니게 된다. 책을 읽으며 처음에는 "어차피 방화벽 뒤에 있는 내부망이고 외부에서 접근이 차단되어 있으니 평문이어도 괜찮은 거겠군"이라고 생각을 했었다.
그러다 문득 망분리는 만병통치약이 아니라는 트윗이 기억이 났다.
https://x.com/simnalamburt/status/1823610803846517196?s=20
내부망이 안전하다는 가정은 내부자 위협, 자격 증명 탈취를 통한 침해, 그리고 한 시스템이 뚫린 후 내부망을 통해 다른 시스템으로 확산되는 횡적 이동 공격과 같은 위험이 도사리고 있다. 이른바 '침해 가정' 원칙에 따라, 공격자가 이미 망 내부에 들어와 있다는 전제로 보안 체계를 설계해야 한다.
Zero Trust 아키텍처
"결코 신뢰하지 말고 항상 검증하라(Never trust, always verify)"는 원칙에 따라 내부망과 외부망을 구분하지 않고 모든 접근에 대해 인증과 권한 검증을 수행한다. 마이크로 세그멘테이션을 통해 네트워크를 세분화하고 최소 권한 원칙을 적용한다.
mTLS
mTLS는 클라이언트와 서버가 서로의 인증서를 검증하여 양방향으로 신원을 확인하는 상호 인증 방식이다. 일반 TLS는 서버만 인증서를 제시하지만 mTLS에서는 클라이언트도 인증서를 제시해야 한다. Zero Trust 아키텍처에서 mTLS는 핵심 구성요소로 내부 서비스 간 통신에서도 모든 요청의 신원을 검증하여 "항상 검증하라"는 원칙을 기술적으로 구현한다.
그럼 굳이 TLS 오프로드 하지 말고 백엔드까지 암호화된 패킷을 전달하면 되는거 아닌가?
TLS 오프로드는 여전히 유효한 선택이다. L7 로드밸런서가 HTTP 헤더나 URL 경로, 쿠키를 분석해서 트래픽을 라우팅하려면 패킷 내용을 들여다볼 수 있어야 한다. WAF도 SQL 인젝션이나 XSS 같은 공격 패턴을 탐지하려면 평문 상태의 요청을 검사해야 한다. 암호화된 상태로는 이런 기능들이 불가능하다.
결국 L7 기능을 활용하려면 어디선가는 TLS를 종료해야 한다. 이때 공인 인증서 처리를 로드밸런서로 집중시키면 보안 정책 적용과 인증서 갱신이 훨씬 수월해진다.
만약 내부망 보안을 위해 재암호화가 필요한 TLS 브릿징(TLS Bridging) 방식을 사용하더라도 오프로드의 이점은 여전하다. 외부 노출용 공인 인증서는 로드밸런서가 전담하고, 내부 구간은 사설 CA나 자동화된 인증서 발급 체계(mTLS 등)를 이용해 백엔드 서버의 관리 부담을 최소화하면서도 보안과 가시성을 모두 챙길 수 있기 때문이다.
洪 民憙 (Hong Minhee) shared the below article:
Hackers' Pub 신고(flag) 기능 기획서
洪 民憙 (Hong Minhee) @hongminhee@hackers.pub
개요
목적
신고 기능은 Hackers' Pub 커뮤니티의 행동 강령(code of conduct)을 위반하는 콘텐츠나 사용자를 식별하고, 관리자가 적절한 조치를 취할 수 있도록 돕는 시스템입니다.
핵심 철학
신고 기능의 궁극적인 목적은 계도와 성장입니다. 무균실처럼 완벽한 사용자만을 남기려는 것이 아니라, 신고를 통해 각자의 행동을 돌아보고 더 나은 커뮤니티 구성원으로 성장할 수 있는 기회를 제공하는 데 있습니다.
추방은 최후의 수단이며, 시스템은 다음과 같은 단계적 접근을 권장합니다:
- 인지 — 피신고자가 자신의 행동이 문제가 될 수 있음을 알게 됩니다
- 성찰 — 왜 그 행동이 문제인지 이해할 기회를 갖습니다
- 개선 — 행동을 수정하고 커뮤니티와 조화롭게 참여합니다
- 제재 — 개선 의지가 없거나 심각한 위반의 경우에만 적용됩니다
분산형 네트워크 고려
Hackers' Pub은 ActivityPub 프로토콜 기반의 분산형 소셜 네트워크입니다. 따라서 신고 기능도 다음을 고려하여 설계되었습니다:
- 다른 서버(인스턴스)에 호스팅된 콘텐츠도 신고 가능
- Mastodon 등 주요 ActivityPub 플랫폼과의 호환성
- 연합(federation) 환경에서의 조치 전파
설계 원칙
신고자 보호
- 원칙
- 신고자의 신원은 철저히 비공개로 유지됩니다.
- 근거
- 신고자가 보복을 두려워하면 신고를 주저하게 되고, 이는 커뮤니티 건강성을 해칠 수 있습니다. 익명성이 보장되어야 신고 시스템이 효과적으로 작동합니다.
- 구현
-
- 피신고자에게는 신고 사실과 사유만 전달되며, 신고자 정보는 공개되지 않습니다
- 관리자만 신고자 정보에 접근할 수 있습니다
- 데이터베이스 수준에서도 접근 제어가 적용됩니다
피신고자의 알 권리
- 원칙
- 피신고자는 자신이 왜 신고되었는지 알 권리가 있습니다.
- 근거
- 무엇이 문제인지 알지 못하면 개선할 수 없습니다. 계도라는 목적을 달성하려면 피신고자가 자신의 행동을 돌아볼 수 있는 충분한 정보를 제공해야 합니다.
- 구현
-
- 신고 사유(행동 강령 위반 내용)가 피신고자에게 전달됩니다
- 어떤 콘텐츠가 문제가 되었는지 명시됩니다
- 단, 신고자가 누구인지는 알 수 없습니다
행동 강령의 유연한 참조
- 원칙
- 행동 강령은 살아있는 문서이며, 시간이 지남에 따라 발전하고 변화할 수 있습니다.
- 근거
- 커뮤니티가 성장하고 사회적 맥락이 변화함에 따라 행동 강령도 함께 진화해야 합니다. 신고 시스템은 이러한 변화에 유연하게 대응할 수 있어야 합니다.
- 구현
-
- 신고 사유를 특정 조항 번호에 하드코딩하지 않습니다
- 신고 시점의 행동 강령 버전을 기록하여 맥락을 보존합니다
- LLM 매칭 시 현재 행동 강령 전문을 참조하여 동적으로 분석합니다
- 신고자가 작성한 원본 사유는 항상 보존됩니다
투명한 처리 과정
- 원칙
- 신고의 처리 과정과 결과는 관련 당사자에게 투명하게 공유됩니다.
- 근거
- 신고자는 자신의 신고가 어떻게 처리되었는지 알 권리가 있으며, 피신고자도 어떤 조치가 취해졌는지 알아야 합니다.
- 구현
-
- 신고자에게 처리 진행 상황과 최종 결과가 통보됩니다
- 피신고자에게 조치 내용과 사유가 전달됩니다
- 관리자의 판단 근거가 기록됩니다
단계적 제재
- 원칙
- 제재는 위반의 심각성과 빈도에 비례하여 단계적으로 적용됩니다.
- 근거
- 경미한 위반에 과도한 제재를 가하면 커뮤니티 참여를 위축시키고, 심각한 위반에 가벼운 제재를 가하면 커뮤니티 안전을 해칩니다.
- 구현
-
- 경고 → 콘텐츠 검열 → 일시 정지 → 영구 정지의 단계적 체계
- 위반 이력이 누적되어 다음 제재 수준에 반영됩니다
- 심각한 위반은 단계를 건너뛰고 즉각적인 강력한 조치 가능
용어 정의
| 용어 | 정의 |
|---|---|
| 신고(flag/report) | 행동 강령 위반으로 의심되는 콘텐츠나 사용자를 관리자에게 알리는 행위 |
| 신고자(reporter) | 신고를 제출하는 사용자 |
| 피신고자(reported) | 신고의 대상이 되는 사용자 |
| 신고 대상(target) | 신고된 콘텐츠(게시글, 단문) 또는 사용자 |
| 관리자(moderator) | 신고를 검토하고 조치를 취할 권한이 있는 사용자 |
| 조치(action) | 관리자가 신고에 대해 취하는 결정 (기각, 경고, 검열, 정지 등) |
| 이의 제기(appeal) | 피신고자가 조치에 대해 재검토를 요청하는 행위 |
| 로컬 사용자 | Hackers' Pub에 계정이 있는 사용자 |
| 원격 사용자 | 다른 ActivityPub 인스턴스의 사용자 |
신고 대상
콘텐츠 신고
사용자는 다음 유형의 콘텐츠를 개별적으로 신고할 수 있습니다:
게시글(article) 신고
- 대상
- 장문의 블로그 형식 게시글
- 표시 위치
- 게시글 하단 또는 더보기 메뉴에 “신고하기” 옵션
- 신고 시 수집 정보
-
- 게시글 ID 및 영구 링크
- 게시글 작성자 정보
- 신고 시점의 게시글 내용 스냅샷 (증거 보존)
- 신고자가 작성한 사유
단문(note) 신고
- 대상
- 짧은 마이크로블로그 형식 글
- 표시 위치
- 단문의 더보기 메뉴에 “신고하기” 옵션
- 신고 시 수집 정보
- 게시글과 동일
사용자 신고
특정 사용자의 전반적인 행동 패턴이 문제가 되는 경우, 개별 콘텐츠가 아닌 사용자 자체를 신고할 수 있습니다.
- 사용 시나리오
-
- 여러 콘텐츠에 걸쳐 지속적으로 문제 행동을 보이는 경우
- 개별 콘텐츠는 경계선상에 있지만, 전체적인 패턴이 문제인 경우
- 프로필 자체(이름, 약력, 프로필 사진 등)가 행동 강령을 위반하는 경우
- 표시 위치
- 사용자 프로필 페이지의 더보기 메뉴에 “사용자 신고하기” 옵션
- 신고 시 수집 정보
-
- 사용자 ID 및 프로필 링크
- 신고 시점의 프로필 정보 스냅샷
- 신고자가 작성한 사유
- (선택) 관련 콘텐츠 링크 첨부 가능
원격 콘텐츠 및 사용자
다른 ActivityPub 인스턴스의 콘텐츠와 사용자도 동일하게 신고할 수 있습니다.
- 근거
- 연합 타임라인에 표시되는 모든 콘텐츠는 Hackers' Pub 사용자에게 영향을 미치므로, 원격 콘텐츠도 신고 대상이 되어야 합니다.
- 처리 방식
-
- Hackers' Pub 내에서의 표시/연합 여부에 대한 조치
- 원격 인스턴스로 ActivityPub
Flag액티비티 전송 (선택적)
신고 프로세스
신고 흐름도
flag_process start 사용자가 콘텐츠/사용자 신고 클릭 form 신고 양식 표시 start->form reason 사유 작성 (자유 형식) form->reason submit 신고 제출 reason->submit llm LLM이 사유를 분석 submit->llm coc 행동 강령 조항 매칭 llm->coc save 신고 저장 (대기 상태) llm->save notify_mod 관리자에게 알림 발송 save->notify_mod notify_reporter 신고자에게 접수 확인 notify_mod->notify_reporter신고 양식
신고 양식은 간결하면서도 필요한 정보를 수집할 수 있도록 설계됩니다.
필수 입력 항목
신고 사유 (자유 형식 텍스트)
이 콘텐츠/사용자를 신고하는 이유를 설명해 주세요.
구체적인 행동 강령 조항을 알지 못해도 괜찮습니다.
어떤 점이 불편하거나 문제가 된다고 느꼈는지
자유롭게 작성해 주세요.
[ ]
[ ]
[ ]
최소 10자 이상 작성해 주세요.
근거:
- 사용자가 행동 강령의 모든 조항을 숙지하고 있다고 가정하지 않습니다
- 자유 형식으로 작성하면 더 풍부한 맥락을 수집할 수 있습니다
- LLM이 사유를 분석하여 관련 조항을 자동으로 매칭합니다
선택 입력 항목
추가 콘텐츠 링크 (사용자 신고 시)
관련된 다른 콘텐츠가 있다면 링크를 추가해 주세요. (선택)
[링크 추가 +]
근거: 사용자 신고의 경우, 문제 행동의 패턴을 보여주는 여러 콘텐츠를 함께 제출하면 관리자가 더 정확한 판단을 내릴 수 있습니다.
LLM 기반 행동 강령 매칭
신고가 제출되면 LLM이 신고 사유를 분석하여 관련된 행동 강령 조항을 식별합니다.
매칭 프로세스
-
입력 구성
- 신고자가 작성한 사유 텍스트
- 현재 버전의 행동 강령 전문
- 신고된 콘텐츠 내용 (있는 경우)
-
LLM 분석
- 신고 사유와 행동 강령 조항 간의 관련성 분석
- 관련 조항 식별 및 신뢰도 점수 산출
- 분석 요약 생성
-
결과 저장
- 매칭된 조항 목록 (신뢰도 점수 포함)
- LLM 분석 요약
- 신고 시점의 행동 강령 버전 식별자
행동 강령 버전 관리
- 근거
- 행동 강령이 변경되면 과거 신고의 맥락이 불명확해질 수 있습니다. 따라서 신고 시점의 행동 강령 버전을 기록하여 맥락을 보존합니다.
- 구현 방식
-
- 행동 강령 파일의 Git 커밋 해시를 버전 식별자로 사용
- 신고 기록에 버전 식별자 저장
- 관리자가 신고를 검토할 때 해당 버전의 행동 강령 참조 가능
매칭 결과 활용
- 관리자 검토
- 매칭 결과는 관리자의 참고 자료로 활용됩니다
- 최종 판단
- 관리자가 매칭 결과를 수정하거나 무시할 수 있습니다
- 피신고자 통보
- 최종 확정된 위반 조항이 피신고자에게 전달됩니다
중복 신고 처리
같은 콘텐츠나 사용자에 대해 여러 신고가 접수될 수 있습니다.
- 처리 방식
-
- 동일 대상에 대한 신고는 하나의 “신고 케이스”로 그룹화됩니다
- 각 신고의 사유는 개별적으로 보존됩니다
- 관리자에게는 신고 건수와 함께 표시됩니다
- 신고 건수가 많을수록 우선순위가 높아집니다
- 근거
-
- 여러 사람이 독립적으로 같은 문제를 발견했다면 더 심각한 문제일 가능성이 높습니다
- 다양한 관점의 신고 사유를 종합하면 더 정확한 판단이 가능합니다
신고 내역 조회
신고자는 자신이 제출한 신고의 상태를 확인할 수 있습니다.
- 확인 가능한 정보
-
- 신고 대상 (콘텐츠/사용자)
- 신고 일시
- 자신이 작성한 신고 사유
- 처리 상태 (대기 중 / 검토 중 / 처리 완료)
- 처리 결과 (조치됨 / 기각됨)
- 확인 불가능한 정보
-
- 다른 신고자의 존재 여부
- 구체적인 제재 내용 (프라이버시 보호)
- 피신고자의 이의 제기 내용
관리자 처리 프로세스
신고 검토 흐름도
moderation_process cluster_review 콘텐츠/사용자 검토 pending 신고 접수 (대기 상태) check 관리자가 신고 확인 pending->check reviewing 검토 시작 (검토 중) check->reviewing review1 신고된 콘텐츠 확인 review2 신고 사유 검토 review3 LLM 매칭 결과 참고 review4 사용자 이력 확인 review5 맥락 파악 decision 판단 결정 review5->decision dismiss 기각 decision->dismiss warn 경고 decision->warn action 제재 decision->action notify 조치 기록 및 알림 - 신고자에게 결과 통보 - 피신고자에게 조치 통보 - (필요시) 원격 서버 통보 dismiss->notify warn->notify action->notify신고 상태
| 상태 | 설명 |
|---|---|
pending |
신고가 접수되어 검토 대기 중 |
reviewing |
관리자가 검토 중 |
resolved |
처리 완료 (조치됨) |
dismissed |
기각됨 (위반 아님) |
검토 시 확인 사항
관리자는 다음 정보를 종합적으로 검토합니다:
신고 정보
- 신고자가 작성한 사유
- LLM이 매칭한 행동 강령 조항
- 신고 건수 (중복 신고의 경우)
- 각 신고자의 사유 (중복 신고의 경우)
콘텐츠 정보
- 신고된 콘텐츠 원문
- 콘텐츠의 맥락 (댓글 스레드 등)
- 신고 시점의 스냅샷 (수정/삭제된 경우)
사용자 정보
- 피신고자의 이전 위반 이력
- 이전 경고/제재 기록
- 계정 생성일 및 활동 기간
- 로컬/원격 사용자 여부
조치 옵션
관리자는 다음 조치 중 하나를 선택합니다:
| 조치 | 설명 | 적용 기준 |
|---|---|---|
| 기각 | 위반이 아니라고 판단 | 행동 강령 위반 사실이 없는 경우 |
| 경고 | 경고 메시지 발송 | 경미한 위반, 초범인 경우 |
| 콘텐츠 검열 | 해당 콘텐츠 숨김 처리 | 콘텐츠 자체가 문제인 경우 |
| 일시 정지 | 일정 기간 계정 정지 | 반복 위반 또는 중간 수준의 위반 |
| 영구 정지 | 계정 영구 정지 | 심각한 위반 또는 지속적 악의적 행동 |
조치 시 필수 입력 사항
관리자가 조치를 취할 때 다음을 기록해야 합니다:
위반 조항 (최종 확정):
[행동 강령 내 관련 조항 선택/입력]
조치 사유:
[관리자의 판단 근거를 상세히 기술]
피신고자에게 전달할 메시지:
[피신고자가 받을 통보 내용]
(일시 정지의 경우) 정지 기간:
[시작일] – [종료일]
근거:
- 조치의 투명성을 확보합니다
- 이의 제기 시 검토 자료로 활용됩니다
- 일관된 판단 기준을 유지하는 데 도움이 됩니다
피신고자 프로세스
신고 통보
피신고자는 자신이 신고되었다는 사실과 사유를 알림으로 받습니다.
통보 시점
즉시 통보하지 않는 경우:
- 신고 접수 직후에는 피신고자에게 통보하지 않습니다
- 무분별한 신고로 인한 불필요한 스트레스 방지
통보하는 경우:
- 관리자가 신고를 검토하고 조치를 결정한 후 통보합니다
- 기각된 경우에도 교육적 목적으로 통보할 수 있습니다 (관리자 재량)
통보 내용
경고/제재 시:
귀하의 [콘텐츠/계정]에 대해 신고가 접수되어 검토한 결과,
행동 강령 위반으로 판단되어 다음과 같은 조치가 취해졌습니다.
위반 내용:
[행동 강령의 관련 조항]
대상 콘텐츠:
[해당되는 경우 콘텐츠 링크]
조치:
[경고 / 콘텐츠 검열 / N일 정지 / 영구 정지]
관리자 메시지:
[관리자가 작성한 설명]
이 조치에 대해 이의가 있으시면 아래 버튼을 통해
이의 제기를 하실 수 있습니다.
[이의 제기하기]
기각 통보 시 (선택적):
귀하의 [콘텐츠/계정]에 대해 신고가 접수되었으나,
검토 결과 행동 강령 위반에 해당하지 않는다고 판단되었습니다.
다만, 일부 커뮤니티 구성원이 불편함을 느꼈을 수 있으므로
참고해 주시면 감사하겠습니다.
관련 내용:
[간략한 설명]
피신고자가 확인할 수 있는 정보
| 정보 | 확인 가능 여부 |
|---|---|
| 신고된 사실 | 가능 |
| 위반으로 지적된 행동 강령 조항 | 가능 |
| 대상 콘텐츠 | 가능 |
| 조치 내용 및 기간 | 가능 |
| 관리자의 판단 사유 | 가능 |
| 신고자가 누구인지 | 불가능 |
| 신고자가 작성한 원본 사유 | 불가능 |
| 신고 건수 | 불가능 |
근거: 피신고자에게 개선에 필요한 정보는 모두 제공하되, 신고자를 특정할 수 있는 정보는 철저히 보호합니다.
제재 중 제한 사항
콘텐츠 검열
- 해당 콘텐츠가 타임라인과 검색에서 숨겨집니다
- 직접 링크(퍼머링크)로는 접근 가능하지만, 검열 안내가 표시됩니다
- 작성자 본인은 콘텐츠를 볼 수 있습니다
일시 정지
- 새로운 콘텐츠 작성 불가
- 댓글 작성 불가
- 반응 불가
- 팔로/언팔로 불가
- 기존 콘텐츠 열람은 가능
- DM 수신은 가능하나 발신 불가
영구 정지
- 계정 접근 불가
- 모든 기능 사용 불가
- 기존 콘텐츠는 숨김 처리됨
이의 제기 프로세스
이의 제기 자격
- 조치를 받은 피신고자만 이의 제기 가능
- 하나의 조치에 대해 1회의 이의 제기 가능
- 이의 제기 기한: 조치 통보 후 14일 이내
이의 제기 흐름도
appeal_process start 피신고자가 이의 제기 write 이의 내용 작성 start->write submit 이의 제출 write->submit review 관리자 검토 (다른 관리자 권장) submit->review decision 판단 review->decision reject 기각 decision->reject uphold 조치 유지 decision->uphold modify 조치 변경 decision->modify notify 결과 통보 (피신고자, 원신고자) reject->notify uphold->notify modify->notify이의 제기 양식
이의 제기 사유:
[왜 이 조치가 부당하다고 생각하시는지 설명해 주세요]
추가 맥락 또는 증거:
[조치 결정 시 고려되지 않았다고 생각되는
맥락이나 정보가 있다면 제공해 주세요]
[제출]
이의 제기 검토
- 검토 원칙
-
- 가능하면 원래 조치를 결정한 관리자가 아닌 다른 관리자가 검토합니다
- 원 신고 내용, 조치 사유, 이의 제기 내용을 종합적으로 검토합니다
- 새로운 정보나 맥락이 있는지 확인합니다
- 결정 옵션
-
- 이의 기각: 원 조치 유지
- 조치 완화: 더 가벼운 조치로 변경 (예: 정지 → 경고)
- 조치 철회: 조치 취소 및 기록 정정
- 조치 강화: 드문 경우, 이의 제기 과정에서 더 심각한 위반이 발견된 경우
결과 통보
피신고자에게:
귀하의 이의 제기를 검토한 결과를 알려드립니다.
결정: [이의 기각 / 조치 완화 / 조치 철회]
판단 사유:
[관리자의 검토 결과 설명]
(해당 시) 변경된 조치:
[새로운 조치 내용]
원 신고자에게:
귀하가 신고하신 건에 대해 피신고자로부터
이의 제기가 있어 재검토가 진행되었습니다.
재검토 결과: [원 조치 유지 / 조치 변경]
(조치가 변경된 경우)
변경 사유에 대한 간략한 설명:
[설명]
패널티 체계
패널티 종류 및 기준
경고 (warning)
- 설명
- 위반 사실을 알리고 재발 방지를 요청하는 가장 가벼운 조치입니다.
- 적용 기준
-
- 경미한 행동 강령 위반
- 초범이며 악의가 없어 보이는 경우
- 실수나 무지로 인한 위반으로 판단되는 경우
- 효과
-
- 경고 메시지가 발송됩니다
- 경고 이력이 기록되어 향후 판단에 참고됩니다
- 일정 기간(예: 1년) 경과 후 이력에서 제외될 수 있습니다
- 경고 누적
-
- 경고 3회 누적 시 자동으로 더 강한 조치 검토 대상이 됩니다
- 단, 자동 제재는 없으며 관리자의 판단이 필요합니다
콘텐츠 검열 (content censorship)
- 설명
- 특정 콘텐츠를 공개 영역에서 숨기는 조치입니다.
- 적용 기준
-
- 콘텐츠 자체가 행동 강령을 위반하는 경우
- 사용자의 전반적 행동보다 특정 콘텐츠가 문제인 경우
- 효과
-
- 해당 콘텐츠가 타임라인, 검색, 추천에서 제외됩니다
- 퍼머링크는 유지되나, 접근 시 검열 안내가 표시됩니다
- 연합(federation)으로 다른 서버에
Delete액티비티가 전송될 수 있습니다
검열 콘텐츠 표시
이 콘텐츠는 행동 강령 위반으로 검열되었습니다.
[원문 보기] (클릭 시 경고와 함께 표시)
일시 정지 (temporary suspension)
- 설명
- 일정 기간 동안 계정 활동을 제한하는 조치입니다.
- 적용 기준
-
- 경고에도 불구하고 위반이 반복되는 경우
- 중간 수준의 심각한 위반인 경우
- 즉각적인 활동 중단이 필요하지만 영구 정지까지는 아닌 경우
- 정지 기간
-
- 최소 1일 – 최대 90일
- 관리자가 위반 정도에 따라 결정
- 권장 기준:
- 경미한 반복 위반: 1–7일
- 중간 수준 위반: 7–30일
- 심각한 위반 (초범): 30–90일
- 효과
-
- 새 콘텐츠 작성 불가
- 상호작용(반응, 댓글 등) 불가
- 기존 콘텐츠 열람은 가능
- 정지 해제 시 완전한 기능 복구
- 원격 사용자의 경우
-
- Hackers' Pub 내에서 해당 기간 동안 연합 차단
- 원격 서버 관리자에게 ActivityPub
Flag액티비티로 통보
영구 정지 (permanent suspension)
- 설명
- 계정을 영구적으로 비활성화하는 가장 강력한 조치입니다.
- 적용 기준
-
- 매우 심각한 행동 강령 위반 (혐오 발언, 불법 콘텐츠 등)
- 일시 정지 후에도 동일한 위반이 반복되는 경우
- 명백한 악의를 가지고 커뮤니티를 해치려는 의도가 확인된 경우
- 효과
-
- 계정 로그인 불가
- 모든 기능 사용 불가
- 공개 콘텐츠 숨김 처리
- 프로필 페이지에 정지 안내 표시
- 원격 사용자의 경우
-
- Hackers' Pub과의 영구적 연합 차단
- 원격 서버 관리자에게 ActivityPub
Flag액티비티로 통보
- 복구
-
- 원칙적으로 영구 정지는 복구되지 않습니다
- 극히 예외적인 경우, 충분한 시간 경과 후 재심 요청 가능
패널티 이력 관리
| 패널티 | 이력 보존 기간 | 비고 |
|---|---|---|
| 경고 | 1년 | 1년간 추가 위반 없으면 이력에서 제외 |
| 콘텐츠 검열 | 무기한 | 콘텐츠 존재하는 한 유지 |
| 일시 정지 | 무기한 | 기록은 유지, 판단 시 경과 시간 고려 |
| 영구 정지 | 무기한 | - |
ActivityPub 연합 처리
개요
Hackers' Pub은 ActivityPub 프로토콜을 사용하는 분산형 네트워크의 일부입니다. 신고 기능도 이 환경에서 원활히 작동해야 합니다.
Flag 액티비티
ActivityPub 명세에는 Flag 액티비티가 정의되어 있으며, 이를 통해 신고를
연합 네트워크에 전파할 수 있습니다.
Flag 액티비티 구조:
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Flag",
"actor": "https://hackerspub.example/users/moderator",
"object": [
"https://remote.example/users/reported_user",
"https://remote.example/posts/problematic_post"
],
"content": "Violation of Code of Conduct: harassment"
}
원격 콘텐츠 신고 처리
신고 접수
- 로컬 사용자가 원격 콘텐츠/사용자를 신고합니다
- 신고는 Hackers' Pub 데이터베이스에 저장됩니다
- 관리자가 일반 신고와 동일하게 검토합니다
조치 적용
-
Hackers' Pub 내 조치:
- 해당 콘텐츠의 로컬 캐시 숨김/삭제
- 해당 사용자와의 연합 차단 (일시/영구)
-
원격 서버 통보 (선택적):
Flag액티비티를 원격 서버에 전송- 원격 서버의 조치 여부는 해당 서버의 재량
외부에서 받은 Flag 처리
다른 서버에서 Hackers' Pub으로 Flag 액티비티가 전송된 경우:
Flag액티비티 수신 및 파싱- 신고 대상이 로컬 사용자/콘텐츠인지 확인
- 관리자에게 외부 신고로 표시하여 알림
- 관리자가 검토 후 자체 판단에 따라 조치
외부 신고 표시:
[외부 신고] remote.example에서 접수됨
대상: @localuser의 콘텐츠
사유: "Violation of our community guidelines"
* 이 신고는 외부 서버에서 접수되었습니다.
자체 행동 강령에 따라 판단해 주세요.
Mastodon 호환성
Mastodon은 가장 널리 사용되는 ActivityPub 구현체입니다. Mastodon과의 호환성을 위해 다음을 고려합니다:
- Mastodon의
Flag액티비티 형식 지원 - Mastodon 관리자 API와의 연동 고려 (향후)
- Mastodon에서 보내는 신고 수신 및 처리
알림 체계
알림 유형
| 알림 유형 | 수신자 | 내용 |
|---|---|---|
flag_received |
관리자 | 새 신고 접수됨 |
flag_resolved |
신고자 | 신고 처리 완료됨 |
action_taken |
피신고자 | 조치가 취해짐 |
appeal_received |
관리자 | 이의 제기 접수됨 |
appeal_resolved |
피신고자 | 이의 제기 처리 완료됨 |
appeal_result |
신고자 | 이의 제기로 인한 변경 알림 |
suspension_ending |
피신고자 | 정지 해제 임박 알림 |
알림 채널
- 인앱 알림
- 기본 알림 방식
- 이메일
- 중요 알림 (조치, 정지 등)
- ActivityPub
- 원격 사용자의 경우 해당 서버로 전송
프라이버시 및 보안
신고자 익명성 보호
- 원칙
- 신고자의 신원은 피신고자에게 절대 공개되지 않습니다.
- 기술적 조치
-
- API 응답에서 신고자 정보 필터링
- 관리자 UI에서만 신고자 정보 표시
- 로그에서 신고자 정보 마스킹 (필요시)
데이터 접근 제어
| 역할 | 접근 가능 정보 |
|---|---|
| 일반 사용자 | 자신의 신고 내역만 |
| 피신고자 | 자신에 대한 조치 및 사유 (신고자 정보 제외) |
| 관리자 | 모든 신고 정보 (신고자 정보 포함) |
콘텐츠 스냅샷
신고 시점의 콘텐츠를 스냅샷으로 저장하는 이유:
- 피신고자가 콘텐츠를 수정/삭제해도 원본 증거 보존
- 공정한 판단을 위한 기록 유지
- 이의 제기 시 참고 자료로 활용
- 보존 기간
-
- 케이스 종료 후 최소 1년간 보존
- 법적 요구사항이 있는 경우 더 오래 보존
악용 방지
- 허위 신고 방지
-
- 동일 사용자의 동일 대상 반복 신고 제한
- 허위 신고 시 신고자에 대한 제재 가능
- 신고 패턴 모니터링
- 신고 폭주 방지
-
- 단시간 다수 신고 시 속도 제한
- 관리자에게 이상 패턴 경고
관리자 대시보드
대시보드 개요
관리자 대시보드는 신고 관리의 중심 허브입니다.
- 주요 화면
-
- 대기 중인 신고 목록
- 신고 상세 및 처리 화면
- 이의 제기 목록
- 통계 및 분석
- 제재 중인 사용자 목록
신고 목록 화면
┌─────────────────────────────────────────────────────────┐
│ 신고 관리 [통계 보기] │
├─────────────────────────────────────────────────────────┤
│ 필터: [전체 ▼] [대기 중 ▼] [최신순 ▼] 검색: [____]│
├─────────────────────────────────────────────────────────┤
│ │
│ ⚠️ 높은 우선순위 (신고 5건 이상) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🔴 @user123의 콘텐츠 (신고 7건) │ │
│ │ "혐오 발언", "차별적 표현" 외 5건 │ │
│ │ 최초 신고: 2시간 전 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 일반 신고 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🟡 @remote@other.server 사용자 (신고 2건) │ │
│ │ "스팸 행위" │ │
│ │ 최초 신고: 5시간 전 │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🟢 @newuser의 댓글 (신고 1건) │ │
│ │ "부적절한 언어 사용" │ │
│ │ 신고: 1일 전 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
신고 상세 화면
┌─────────────────────────────────────────────────────────┐
│ 신고 상세 - 케이스 #12345 [← 목록] │
├─────────────────────────────────────────────────────────┤
│ │
│ 📋 기본 정보 │
│ ──────────────────────────────────────── │
│ 대상: @user123의 콘텐츠 │
│ 유형: 단문 (note) │
│ 신고 건수: 7건 │
│ 상태: 대기 중 │
│ │
│ 📝 신고된 콘텐츠 │
│ ──────────────────────────────────────── │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [콘텐츠 원문 표시] │ │
│ │ 작성일: 2024-12-01 14:30 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 🔍 신고 사유 (7건) │
│ ──────────────────────────────────────── │
│ 1. "명백한 혐오 발언입니다" - 신고자A, 2시간 전 │
│ 2. "특정 집단을 비하하는 표현" - 신고자B, 3시간 전 │
│ 3. "불쾌한 차별적 언어" - 신고자C, 4시간 전 │
│ ... (더 보기) │
│ │
│ 🤖 LLM 분석 결과 │
│ ──────────────────────────────────────── │
│ 관련 행동 강령 조항: │
│ - 차별 금지 (신뢰도: 95%) │
│ - 존중하는 언어 사용 (신뢰도: 88%) │
│ │
│ 📊 피신고자 이력 │
│ ──────────────────────────────────────── │
│ - 가입일: 2024-06-15 │
│ - 이전 경고: 1회 (2024-09-20) │
│ - 이전 정지: 없음 │
│ │
│ ⚡ 조치 │
│ ──────────────────────────────────────── │
│ [기각] [경고] [콘텐츠 검열] [일시 정지] [영구 정지] │
│ │
└─────────────────────────────────────────────────────────┘
통계 화면
기간 선택 드롭다운으로 조회 범위를 설정합니다 (예: 최근 30일).
요약
| 항목 | 값 |
|---|---|
| 총 신고 건수 | 127건 |
| 처리 완료 | 98건 (77%) |
| 평균 처리 시간 | 4.2시간 |
조치 분포
| 조치 | 건수 | 비율 |
|---|---|---|
| 기각 | 45건 | 46% |
| 경고 | 38건 | 39% |
| 콘텐츠 검열 | 10건 | 10% |
| 일시 정지 | 4건 | 4% |
| 영구 정지 | 1건 | 1% |
위반 유형 (상위 5개)
| 순위 | 유형 | 건수 |
|---|---|---|
| 1 | 스팸/광고 | 32건 |
| 2 | 혐오 발언 | 24건 |
| 3 | 괴롭힘 | 18건 |
| 4 | 부적절한 콘텐츠 | 12건 |
| 5 | 허위 정보 | 8건 |
향후 고려사항
자동화 기능 (향후 도입 검토)
- 자동 숨김: 특정 임계값 이상의 신고가 접수되면 관리자 검토 전 임시 숨김
- AI 기반 사전 필터링: 명백한 위반 콘텐츠 자동 감지
- 스팸 자동 처리: 명백한 스팸에 대한 자동 조치
주의
자동화 기능은 오탐의 위험이 있으므로 신중하게 도입해야 합니다.
커뮤니티 참여
- 신뢰할 수 있는 신고자: 정확한 신고 이력을 가진 사용자의 신고에 높은 가중치
- 커뮤니티 중재자: 관리자 부담 분산을 위한 커뮤니티 중재자 제도 검토
다국어 지원
- 신고 사유 자동 번역 (관리자가 다른 언어 사용 시)
- 행동 강령 다국어 버전과의 연동
- 조치 통보 메시지 다국어 템플릿
법적 요구사항 대응
- 법적 요청에 따른 데이터 보존/제공 절차
- 저작권 침해 신고 (DMCA 등) 별도 처리 절차
- 사법기관 협조 절차
부록: 용어 대조표
| 한국어 | 영어 | 설명 |
|---|---|---|
| 신고 | flag/report | 위반 의심 콘텐츠/사용자를 알림 |
| 행동 강령 | code of conduct | 커뮤니티 규칙 |
| 관리자 | moderator | 신고 처리 권한자 |
| 검열 | censorship | 콘텐츠 숨김 처리 |
| 정지 | suspension | 계정 활동 제한 |
| 이의 제기 | appeal | 조치에 대한 재검토 요청 |
| 연합 | federation | 분산 네트워크 간 연결 |
| 콘텐츠 | post | 게시글과 단문을 통칭 |
| 게시글 | article | 장문의 블로그 형식 글 |
| 단문 | note | 짧은 마이크로블로그 형식 글 |
| 타임라인 | timeline | 콘텐츠 피드 |
| 팔로 | follow | 다른 사용자 구독 |
| 팔로워 | follower | 나를 구독하는 사용자 |
| 차단 | block | 특정 사용자 접근 제한 |
| 반응 | react | 콘텐츠에 이모지로 반응 |
| 연합우주 | fediverse | ActivityPub 기반 분산 소셜 네트워크 |
| 인스턴스 | instance | 연합우주의 개별 서버 |
이 문서는 Hackers' Pub 커뮤니티의 의견을 수렴하여 지속적으로 개선됩니다.
洪 民憙 (Hong Minhee) shared the below article:
도커로 구축한 랩에서 혼자 실습하며 배우는 네트워크 프로토콜 입문 #5-2 DNS
자손킴 @jasonkim@hackers.pub
이번 섹션은 DNS에 대해서 알아본다. 책의 내용을 기반으로 패킷 분석은 생략하고 DNS의 발전과정, 존파일의 구체적인 예시와 보안 관련 내용을 짧게 추가하여 정리했다.
1장에서는 L2의 MAC 주소에 대해서 이야기했고, 2장에서는 L3의 IP주소에 대해서 이야기했다. IP 주소를 MAC 주소로 변환하기 위해서 ARP(Address Resolution Protocol)를 사용한다.
인터넷에서 다른 단말기에 접속하기 위해서는 IP 주소를 알아야 한다. 그러나 IP 주소는 숫자의 나열이기 때문에 서로 다른 단말기의 IP 주소를 외우고 있는 것은 힘들 뿐더러 IP 주소가 바뀔 경우 다시 외워야 한다는 번거로움이 크다.
DNS는 이러한 불편함을 해결하기 위해 만들어졌다.
DNS(Domain Name System)
인터넷의 전신인 아파넷(ARPANET)에서는 모든 컴퓨터의 이름과 지금의 IP 주소격인 Host Number를 HOSTS.TXT라는 파일에 기록하여 관리했다. 이 파일은 스탠포드 연구소(SRI)의 네트워크 정보 센터(NIC)에서 관리했고, 네트워크 관리자들이 주기적으로 FTP를 통해 이 파일을 다운로드하여 자신의 시스템에 복사했다.
1980년대 초에 네트워크가 급성장하면서 중앙집중식으로 관리되는 HOSTS.TXT는 확장성의 한계에 직면하게 되고 Paul Mockapetris가 RFC 882와 RFC 883을 통해 DNS를 제안한다. 이후 개정을 통해 RFC1034, RFC1035로 표준화 되었다.
DNS는 사람이 읽을 수 있는 도메인 이름을 컴퓨터가 통신에 사용하는 IP 주소로 변환해주는 분산 데이터베이스 시스템이다. 흔히 인터넷의 전화번호부에 비유되며 전 세계에 분산된 수많은 DNS 서버들이 협력하여 도메인 이름에 대한 질의에 응답한다.
도메인 이름
도메인은 계층 구조로 이루어져 있다. 가장 상위에는 루트 도메인이 있고 그 아래로 최상위 도메인(TLD, Top-Level Domain), 2레벨 도메인(2LD, 2nd Level Domain), 3레벨 도메인 순으로 내려간다. 예를 들어 www.example.com이라는 도메인이 있을 때 루트 도메인은 맨 끝의 점(.)으로 표현되며 보통 생략된다.
도메인 이름에서 점(.)으로 구분되는 각각의 부분을 라벨(label)이라고 부른다. www.example.com에서 www, example, com이 각각 하나의 라벨이다. 루트 도메인까지 포함하여 완전하게 표기된 도메인 이름을 FQDN(Fully Qualified Domain Name)이라고 한다.
도메인 이름은 일반적으로 호스트 부분과 도메인 부분으로 나눌 수 있다. www.example.com에서 www는 호스트 부분으로 특정 서버나 서비스를 가리키고, example.com은 도메인 부분으로 해당 조직이나 서비스의 영역을 나타낸다.
최상위 도메인(TLD)은 크게 일반 최상위 도메인(gTLD, Generic Top-Level Domain)과 국가 코드 최상위 도메인(ccTLD, Country Code Top-Level Domain)으로 구분된다. 일반 최상위 도메인은 특정 국가에 속하지 않고 용도나 분야에 따라 구분되는 도메인이다. 국가 코드 최상위 도메인은 ISO 3166-1 alpha-2 표준에 기반한 두 글자 코드로 각 국가나 지역을 나타내며 각 국가의 도메인 등록 기관이 해당 ccTLD의 정책을 독자적으로 관리한다.
DNS의 도메인 체계는 트리 형태의 계층 구조를 이룬다. 맨 꼭대기에는 루트 도메인이 위치하며 이 루트로부터 각 TLD가 가지처럼 뻗어나가고 각 TLD 아래에는 다시 수많은 2단계 도메인이, 그 아래에는 3단계 도메인이 연결되는 식이다. 이 구조에서 각 노드는 자신의 하위 도메인에 대한 정보를 관리하는 권한을 갖는다.
이름 풀이
IP 주소와 도메인 이름을 서로 변환하는 과정이다. 이름 풀이에는 hosts파일이나 DNS를 사용하며 /etc/nsswitch.conf 등에 의해 우선순위가 정해진다.
hosts 파일을 이용한 이름 풀이
hosts 파일은 IP 주소와 호스트 이름의 매핑을 한 줄에 하나씩 기록하는 단순한 텍스트 파일로, 127.0.0.1 localhost처럼 IP 주소 뒤에 공백을 두고 호스트 이름을 적는 형식이다. hosts 파일은 네트워크 연결 없이도 동작하고 설정에 따라서는 DNS보다 우선 적용된다.
운영체제는 이름 풀이 요청이 들어오면 먼저 이 파일을 확인하고 일치하는 항목이 있으면 DNS 질의 없이 바로 해당 IP 주소를 반환한다.
DNS를 이용한 이름 풀이
DNS를 이용한 이름 풀이는 여러 구성요소의 협력으로 이루어진다. 먼저 DNS 클라이언트(일명: 스터브 리졸버(Stub Resolver))는 사용자 컴퓨터의 운영체제에 내장된 DNS 클라이언트로 애플리케이션의 이름 풀이 요청을 받아 DNS 서버에 질의를 보내는 역할을 한다. 스터브 리졸버는 단순히 질의를 전달하고 응답을 받아오는 역할만 한다.
재귀 리졸버(Recursive Resolver) 또는 캐싱 네임서버는 DNS 클라이언트의 재귀 쿼리를 받아 인터넷에 있는 권한이 있는 네임서버에 이름 풀이 요청(반복 쿼리)를 보낸다.
권한이 있는 네임서버(Authoritative Name Server)는 특정 도메인에 대한 공식적인 DNS 레코드를 보유한 서버로 해당 도메인에 대한 질의에 최종적인 답변을 제공한다. 권한이 있는 네임서버는 존 파일(Zone File)이라는 텍스트 파일에 도메인 정보를 저장하며, 이 존 파일에는 리소스 레코드(Resource Record)라고 불리는 개별 항목들이 포함된다. A 레코드는 도메인 이름을 IPv4 주소로 매핑하고 AAAA 레코드는 IPv6 주소로 매핑한다. CNAME 레코드는 별칭을 정의하며 MX 레코드는 메일 서버, NS 레코드는 해당 도메인의 네임서버를 지정한다.
루트 네임서버는 DNS 계층의 최상위에서 TLD 네임서버의 위치를 알려주고 TLD 네임서버는 각 최상위 도메인에 등록된 도메인들의 권한이 있는 네임서버 위치를 안내한다.
DNS의 동작 방식
사용자가 웹 브라우저에 www.example.com을 입력하면 DNS를 통한 이름 풀이 과정이 시작된다. 먼저 운영체제의 스터브 리졸버가 설정된 재귀 리졸버에게 해당 도메인의 IP 주소를 요청한다. 재귀 리졸버는 자신의 캐시를 확인하여 이전에 조회한 적이 있는 도메인이라면 캐시된 결과를 즉시 반환한다.
캐시에 없는 경우 재귀 리졸버는 루트 네임서버에 질의를 보낸다. 루트 네임서버는 www.example.com의 IP 주소를 직접 알지 못하지만 com TLD를 담당하는 네임서버의 주소를 알려준다. 재귀 리졸버는 이어서 com TLD 네임서버에 질의하고 TLD 네임서버는 example.com 도메인의 권한이 있는 네임서버 주소를 응답한다. 마지막으로 재귀 리졸버가 해당 권한이 있는 네임서버에 질의하면 이 서버가 www.example.com의 실제 IP 주소를 담은 최종 답변을 반환한다.
재귀 리졸버는 이 결과를 캐시에 저장한 뒤 클라이언트에게 전달하여 전체 이름 풀이 과정이 완료된다. 캐시에 저장된 레코드는 TTL(Time To Live)이라는 유효 시간 동안 유지되며 TTL이 만료되면 다시 질의를 수행해야 한다.
재귀 쿼리와 반복 쿼리
DNS 질의는 재귀 쿼리(Recursive Query)와 반복 쿼리(Iterative Query)로 구분되며 이름 풀이 과정의 각 단계에서 서로 다른 방식이 사용된다.
재귀 쿼리는 클라이언트가 리졸버에게 최종 답변을 요구하는 질의 방식이다. 스터브 리졸버가 재귀 리졸버에게 질의를 보낼 때 이 방식을 사용한다. 재귀 쿼리를 받은 리졸버는 어떤 방법을 쓰든 완전한 답변을 돌려주거나 찾을 수 없다는 오류를 반환해야 하는 의무를 지게 된다. 클라이언트 입장에서는 한 번의 질의로 최종 결과를 받을 수 있으므로 구현이 단순해지고 복잡한 이름 풀이 로직은 전적으로 재귀 리졸버가 처리하게 된다.
반복 쿼리는 재귀 쿼리와 달리 상대 서버에게 최선의 답변만을 요청하는 방식이다. 재귀 리졸버가 루트 네임서버, TLD 네임서버, 권한이 있는 네임서버에 질의할 때 이 방식을 사용한다. 반복 쿼리를 받은 서버는 자신이 알고 있는 범위 내에서 답을 주거나 더 잘 알 것 같은 다른 서버의 주소를 알려주는 참조 응답(Referral)을 반환한다. 재귀 리졸버는 이 참조 응답을 따라가며 여러 서버에 순차적으로 질의하여 최종 답변을 찾아낸다.
정리하면, 일반적인 DNS 이름 풀이 과정에서 스터브 리졸버와 재귀 리졸버 사이에는 재귀 쿼리가 사용되고 재귀 리졸버와 각종 네임서버들 사이에는 반복 쿼리가 사용된다. 루트 네임서버, TLD 네임서버, 권한이 있는 네임서버와 같은 서버들은 모두 반복 쿼리만을 처리하며 다른 서버를 대신해서 질의를 수행하지 않는다.
A 레코드와 CNAME
리소스 레코드에는 여러 레코드 타입이 있지만 가장 기본이 되는 것은 A 레코드와 CNAME 레코드다.
A 레코드(Address Record)는 도메인 이름을 IPv4 주소에 직접 매핑한다. 사용자가 도메인에 접속할 때 최종적으로 필요한 것이 바로 이 A 레코드에 담긴 IP 주소다. 하나의 도메인에 여러 개의 A 레코드를 설정하여 트래픽을 분산시킬 수도 있다.
CNAME 레코드(Canonical Name Record)는 도메인 이름을 다른 도메인 이름으로 매핑하는 별칭 레코드다. CNAME은 IP 주소를 직접 가리키지 않고 다른 도메인을 가리킨다. 리졸버가 CNAME 레코드를 만나면 해당 별칭이 가리키는 도메인에 대해 다시 질의를 수행하여 최종적으로 A 레코드를 찾아낸다. CNAME은 여러 서브도메인이 같은 서버를 가리켜야 할 때 유용하다. 서버의 IP 주소가 변경되더라도 A 레코드 하나만 수정하면 해당 도메인을 가리키는 모든 CNAME이 자동으로 새 IP를 따라가게 된다.
다음은 example.com 도메인의 존 파일 예시다.
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2024010101 ; Serial
7200 ; Refresh
3600 ; Retry
1209600 ; Expire
3600 ) ; Minimum TTL
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
@ IN A 93.184.216.34
www IN CNAME example.com.
blog IN CNAME example.com.
api IN A 93.184.216.50
; 외부 서비스 연동
cdn IN CNAME d1234abcd.cloudfront.net.
app IN CNAME my-app.vercel.app.
storage IN CNAME my-bucket.s3.amazonaws.com.
ns1 IN A 93.184.216.10
ns2 IN A 93.184.216.11
이 존 파일에서 @는 현재 도메인인 example.com을 의미한다. example.com은 A 레코드를 통해 93.184.216.34라는 IP 주소에 직접 매핑되어 있다. www.example.com과 blog.example.com은 CNAME 레코드로 example.com을 가리키므로, 결과적으로 같은 IP 주소로 연결된다. 반면 api.example.com은 별도의 A 레코드를 가지고 있어 다른 IP 주소를 사용한다.
CNAME은 같은 도메인 내의 다른 이름뿐 아니라 완전히 다른 도메인을 가리킬 수도 있다. 위 예시에서 cdn, app, storage 서브도메인은 각각 외부 서비스 제공자의 도메인을 가리킨다. 이 방식의 장점은 외부 서비스의 실제 IP 주소를 알 필요가 없다는 것이다. 서비스 제공자가 내부적으로 서버를 이전하거나 IP를 변경하더라도 사용자의 DNS 설정은 그대로 유지된다. 서비스 제공자의 네임서버가 항상 현재 유효한 IP 주소를 응답하기 때문이다.
DNS와 보안
DoT와 DoH
전통적인 DNS 질의는 암호화되지 않은 평문으로 전송된다. 이는 네트워크 경로 상의 누군가가 사용자가 어떤 도메인에 접속하려는지 엿볼 수 있고 응답을 조작할 수도 있다는 의미다.
DoT(DNS over TLS)와 DoH(DNS over HTTPS)는 이 문제를 해결하기 위해 DNS 질의를 암호화한다. DoT는 853번 포트를 사용하여 TLS로 DNS 트래픽을 감싸고 DoH는 일반 HTTPS 트래픽과 동일한 443번 포트를 사용한다. DoH는 일반 웹 트래픽과 구분이 어려워 차단하기 힘들다는 특징이 있다.
SNI와 ECH
DNS 질의를 암호화하더라도 여전히 접속하려는 도메인이 노출되는 지점이 있다. HTTPS 연결을 시작할 때 클라이언트는 SNI(Server Name Indication)라는 필드에 접속하려는 도메인 이름을 평문으로 보낸다. 하나의 IP 주소에서 여러 도메인을 호스팅하는 서버가 어떤 인증서를 제시할지 알아야 하기 때문이다.
ECH(Encrypted Client Hello)는 SNI를 포함한 ClientHello 메시지 전체를 암호화하여 이 문제를 해결한다. ECH를 사용하면 네트워크 관찰자는 사용자가 어떤 도메인에 접속하는지 알 수 없게 된다.
@neo 是在用 Ruby 實作新的程式語言嗎?我以前也曾用 Ruby 寫過一個簡單的 Lisp 直譯器,過程真的很有趣。祝您新年快樂!
@hongminhee洪 民憙 (Hong Minhee) 是的,Ruby 是我最熟悉的编程语言,自制编程语言过程充满挑战和乐趣。多年以前,我还是编程新手时,也尝试了解 Lisphp 的实现方式,但由于能力所限,未能做到;有一段时间使用过 CoffeeScript,得知作者起初也是以 Ruby 作为实现语言,我想对于现在的我,Ruby 是最适合的选择。感谢您为编程爱好者提供这样的交流平台,祝新的一年万事如意!
인터페이스가 구린 라이브러리를 쓸때는 반!드!시! 심호흡을 하고 멀쩡한 인터페이스의 wrapper를 만든후에! 기능 개발을 합시다. 절대 대충 꾸역꾸역 만들어보자고 덤비면 안됩니다. 결국 후회합니다.
- V모사의 AI 라이브러리를 쓰다가
Designing type-safe sync/async mode support in TypeScript https://lobste.rs/s/844jrt #api #javascript #plt
https://hackers.pub/@hongminhee/2026/typescript-sync-async-type-safety
바이브코딩이 취미가 되어가는 것일까요? 그새 또 뭔가 하나를 뚝딱여왔습니다... be-music-script라는 동인 리듬게임 에뮬레이터 비슷한 친구의 플레이 로그를 정리해주는 서비스를 만들어봤어요. 많은 리듬게임 유저들은 자기가 얼마나 잘했는지 자랑하고 싶어하는데, db 파일을 읽은 뒤 당일의 멋진 성과들을 정리해주는 서비스입니다. 제가 쓰려고 만든건데 이것도 결국 엔지니어링? 결과물인걸까? 싶어 올려봅니다.
요즘 AI의 도움 덕분에 아이디어를 구현하는게 두렵지 않아졌다는 기분이 드네요. https://sonohoshi.github.io/gosubms/
Claude Code의 인기와 함께 터미널에서 한글을 쓰는 모습을 자주 볼 수 있습니다. 하지만 터미널에서 쓰이는 한글은 글자간의 간격이 넓어 보기 좋지 않은 경우가 많습니다. 왜 이런 걸까요?
흔하게 쓰는 코드용 글꼴은 로마자, 숫자, 특수기호만을 다룰 뿐 한글은 다루지 않습니다. 그래서 터미널은 한글 표시를 하기 위해 대체 글꼴을 사용합니다. 대체 글꼴은 보통 OS의 기본 글꼴일 것입니다. 가변폭 글꼴이겠죠. 터미널은 이를 일부러 고정폭으로 만들기 위해 한글 한 자에 로마자 2자 폭을 할당하는데 이 과정에서 여백이 추가되면서 자간이 넓은 어색한 한글을 보게 되는 것입니다.
해결책은 한글 고정폭 글꼴을 사용하는 것입니다. 한글 고정폭 글꼴은 한글 1자를 로마자 2자 폭에 맞춰 만들었으므로 터미널이 더 이상 여백을 만들지 않습니다. 이러한 한글 고정폭 글꼴이 많진 않습니다. 10종이 안 되는 것 같네요. 저는 그중 Monoplex를 사용하고 있습니다.
@hongminhee洪 民憙 (Hong Minhee) 님은 Sarasa Gothic을 사용하신다고 하네요. 적은 수의 글꼴이지만 맘에 드시는 걸 찾으셔서 예쁜 한글 출력을 보시면 좋겠습니다.
갠적으로 커밋 메시지나 PR 제목 앞에, feat:, fix:, chore: 붙이는 컨벤션은 뭘붙일지 애매한 경우가 너무 많아서 별로라고 본다.
Optique 0.9.0 pre-release is ready for testing!
The big new feature: sync/async mode support. You can now build CLI parsers with async value parsing and suggestions—perfect for shell completions that need to run commands (like listing Git branches/tags).
The API automatically propagates async mode through combinators, so you only decide sync vs async at the leaf level.
Try it:
npm add @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212
deno add --jsr @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212I'd love feedback before merging! Especially interested in:
- API ergonomics
- Edge cases I might have missed
- TypeScript inference issues
Docs:
Optique 0.9.0 pre-release is ready for testing!
The big new feature: sync/async mode support. You can now build CLI parsers with async value parsing and suggestions—perfect for shell completions that need to run commands (like listing Git branches/tags).
The API automatically propagates async mode through combinators, so you only decide sync vs async at the leaf level.
Try it:
npm add @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212
deno add --jsr @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212I'd love feedback before merging! Especially interested in:
- API ergonomics
- Edge cases I might have missed
- TypeScript inference issues
Docs:
Optique 0.9.0 프리릴리스 테스트 중입니다!
이번 주요 기능은 동기/비동기 모드 지원입니다. 이제 비동기 값 파싱과 자동완성을 지원하는 CLI 파서를 만들 수 있습니다. Git 브랜치/태그 목록처럼 셸 명령 실행이 필요한 자동완성에 딱이에요.
컴비네이터를 통해 async 모드가 자동으로 전파되기 때문에, 개발자는 말단 파서에서만 동기/비동기를 결정하면 됩니다.
설치:
npm add @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212
deno add --jsr @optique/core@0.9.0-dev.212 @optique/run@0.9.0-dev.212머지 전에 피드백 주시면 정말 감사하겠습니다! 특히 이런 부분이 궁금해요:
- API 사용성
- 에지 케이스
- TypeScript 타입 추론 문제
문서:
TCP/IP is a social construct
Modern optimizing compilers are truly amazing. Rust / LLVM just broke my brain by turning what I was SURE would be poorly optimized code due to indirection into a tight result with zero perceptible overhead.
Modern CPUs also probably help.
WinGet도 요즈음은 Microsoft Store와 비슷한 정책을 적용하기 시작해서, 자동화된 검사 과정에서 식탁보 새 버전 매니페스트 등록이 막혔었는데, Moderator께서 정확한 판단을 내려주신 덕분에 무사히 새 버전이 게시되었습니다.
안타깝게도 Microsoft Store에는 식탁보를 등록하는 것이 계속 어려울 것으로 보입니다만, 대신 WinGet에 등록이 가능할테니 UniGetUI 등의 수단을 이용해서 커맨드라인 없이 식탁보를 쉽게 설치할 수 있는 방법을 조만간 공식 가이드로 내도록 하겠습니다.
Apple will allow alternative browser engines for iPhone and iPad users (iOS/iPadOS) in Japan.
https://developer.apple.com/support/alternative-browser-engines-jp/
Apple should allow alt engine for the rest of the world too. No point holding it back.
대한수학회 수학달력 2026년을 하루에 하나씩 짧게 설명해보는 타래
"Crafting Interpreters" 真的想教会我写自己的编程语言。
![* 21. Ba5# - 백이 룩과 퀸을 희생한 후, 퀸 대신 **비숍(Ba5)**이 결정적인 체크메이트를 성공시킵니다. 흑 킹이 탈출할 곳이 없으며, 백의 기물로 막을 수도 없습니다. [강조 처리된 "비숍(Ba5)" 앞뒤에 마크다운의 강조 표시 "**"가 그대로 노출되어 있다.]](https://media.hackers.pub/note-media/17646c5d-3f9d-472b-9d56-dd34006ad291.webp)
![* 21. Ba5# - 백이 룩과 퀸을 희생한 후, 퀸 대신 **비숍(Ba5)**이 결정적인 체크메이트를 성공시킵니다. 흑 킹이 탈출할 곳이 없으며, 백의 기물로 막을 수도 없습니다. [The emphasized portion `비숍(Ba5)` is surrounded by unrendered Markdown emphasis marks `**`.]](https://media.hackers.pub/note-media/1e9c2aae-dd7d-4963-b3c8-34336b34dfac.webp)











