염산하

@[email protected] · 23 following · 15 followers

A software engineer in Seoul, and a father of a kid.

염산하

@[email protected] · Reply to 염산하's post

@ysh 이제 정신 차려야지.

염산하

@[email protected]

페북에서 파토님이 절대회귀라는 웹소설이 완결되었는데 아무튼 강추라고해서 시작했다가... 10일만에 다 읽었다. 도저히 적당히 읽을 수가 없어서 거의 모든 시간을 읽는데 쏟았네. 이게 뭐라고 보는내내 울고웃고 너무 몰입했다. 네이버 시리즈. 총 896회. 22년 10월부터 매일 연재한 작품이었네. 장영훈 작가.

염산하

@[email protected]

원래 옵시디언으로 노트를 많이 만들어놨는데 카르파시 선생의 LLM wiki 내용으로 스킬 만들어서 폴더 하나 정해주고 정리시키니까 자미있네

염산하

@[email protected]

정확치도 않은 사실을 포함한채로 AI 생성 문서를 위키에 올려놓으면 ... 쓰레기가 남으면 영원히 삽질의 근원이 될 것 같아서 고쳐야 하는데 기분은 별로고...

https://hollo.woojinkim.org/@me/019cf945-06b1-7dc4-8274-d63ed71d33f4

Woojin Kim's avatar
Woojin Kim

@[email protected]

세심하게 작성되지는 않았지만 어쨌든 필요한 내용을 모두 포함한 문서에 대해 이전같으면 더 잘 쓰는 방법을 피드백 하기도 했지만 ...... 이젠 그냥 기계한테 읽고 정리하라고 하기 때문에 문서를 더 잘 쓰라고 피드백하지 않게 됐다.

염산하

@[email protected] · Reply to Woojin Kim's post

@me 엄청 많은 드론이 필요해지는 거 아닐까요?

염산하

@[email protected] · Reply to Woojin Kim's post

@me 오 진짜요?

염산하

@[email protected]

https://martinfowler.com/fragments/2026-03-16.html

Haze's avatar
Haze

@[email protected]


밖에서 맥북 에어나 아이패드같은 가벼운 기기에서 SSH를 통해 원격 작업을 해보고 싶어져서 테스트를 해보면서 연결이 끊기면 작업하던 내용이 멈추거나 하는 것들이 불편해 세션 유지 도구가 필요하겠다는 생각이 들었습니다. 하지만 저는 예전부터 tmux를 써보려고 여러 차례 시도했지만 어떻게 해도 손에 익지 않아 금방 포기하게 되었는데, 옆에서 이야기를 들으셨던 @hongminhee 님에게 추천을 받아서 Zellij를 알게 되었고 시도해보게 되었습니다.

Zellij를 사용해보니 기대했던 것 이상으로 사용 경험이 좋고 커스터마이징 등 설정도 어렵지 않아서 매우 만족하고 있습니다. 그래서 오늘은 Zellij를 소개하고 저의 경험과 설정도 공유해보려고 합니다.

Zellij란?

Zellij은 Rust로 작성된 터미널 멀티플렉서입니다. tmux와 같은 범주의 도구로 터미널 안에서 탭을 만들거나 화면을 분할하고 세션을 유지할 수 있습니다.

tmux와 비교했을 때 제가 느낀 차이는 진입 장벽입니다. Zellij을 처음 실행하면 화면 하단에 현재 모드에서 쓸 수 있는 단축키가 바로 표시됩니다. 설정 파일은 KDL이라는 포맷을 쓰는데 읽거나 작성하는데 크게 어렵지 않습니다. 모드 기반 인터페이스라는 점은 tmux와 비슷하지만, 뭘 눌러야 하는지 화면에서 바로 알 수 있어서 문서를 뒤질 필요가 적습니다.

처음 설치했을 때의 이미지
Zellij를 처음 설치하면 나오는 이미지

Zellij는 여러 패키지 매니저를 지원하고 Cargo를 이용해 직접 빌드하는 것도 가능합니다. Homebrew가 설치된 macOS 환경이라면 한 줄이면 됩니다.

brew install zellij

키 바인딩과 머슬 메모리

저는 터미널 에뮬레이터에서 제공하는 기능들을 적극적으로 활용하는 편입니다. Ghostty는 자체적으로 탭 관리, 화면 분할, 검색 등 많은 기능을 내장하고 있습니다. 그래서 처음엔 Zellij를 테스트해볼 때, 이런 서로 중복되는 기능을 어떻게 처리할지에 대한 고민도 있었습니다. Ghostty가 앞에서 해당 키를 처리하고 Zellij에겐 해당 이벤트가 전달되지 않겠지만, 같은 단축키로 바인딩하면 어느 쪽의 기능을 써야할지 모호해집니다.

제가 시도한 방법은 터미널 에뮬레이터는 계속 Ghostty를 쓰지만, 이러한 기능은 모두 Zellij에게 위임하는 방향을 선택했습니다. 하지만 Ghostty의 단축키 바인딩을 그대로 써서 단축키에 익숙해지는데 시간을 들이지 않고 싶었습니다. Ghostty에서 unbind 처리를 해주면 모든 단축키 입력도 Zellij까지 전달되어, Zellij에서 이를 받아서 처리하는 형태가 되었습니다. 의외로 설정은 어렵지 않아서 바로 손에 익을 수 있었습니다.

Ghostty의 키 바인딩 해제하기

# ~/.config/ghostty/config

# Zellij에게 넘길 키 바인딩을 전부 해제
keybind = cmd+t=unbind        # 새 탭
keybind = cmd+n=unbind        # 새 탭
keybind = cmd+w=unbind        # 닫기

keybind = cmd+d=unbind        # 세로 분할
keybind = cmd+shift+d=unbind  # 가로 분할

keybind = cmd+f=unbind        # 검색
keybind = cmd+c=unbind        # 복사
keybind = cmd+k=unbind        # 화면 지우기

keybind = cmd+[=unbind        # 이전 Pane
keybind = cmd+]=unbind        # 다음 Pane

keybind = cmd+1=unbind        # 탭 1~9 전환
keybind = cmd+digit_1=unbind
keybind = cmd+2=unbind
keybind = cmd+digit_2=unbind
# ... cmd+9, cmd+digit_9까지 동일

# macOS의 Option 키를 Alt로 다루기 위한 설정
macos-option-as-alt = true

원래부터 있던 폰트나 테마 등의 Ghostty 설정은 수정하지 않았습니다.

익숙한 단축키를 그대로 Zellij에서 쓰기

Ghostty에서 풀어준 키를 Zellij 쪽에서 받습니다. 여기서 제가 신경 쓴 건 하나로, 기존 Ghostty에서의 머슬 메모리를 그대로 유지하는 것입니다.

// ~/.config/zellij/config.kdl

keybinds clear-defaults=false {
    normal {
        // Split — Ghostty와 같은 단축키
        bind "Super d" { NewPane "Right"; SwitchToMode "normal"; }
        bind "Super Shift d" { NewPane "down"; SwitchToMode "normal"; }

        // 탭
        bind "Super t" { NewTab; }
        bind "Super n" { NewTab; }
        bind "Super w" { CloseFocus; }

        // Pane 이동 — Ghostty와 같은 단축키
        bind "Super [" { FocusPreviousPane; }
        bind "Super ]" { FocusNextPane; }

        // 탭 전환 — Ghostty와 같은 단축키
        bind "Super 1" { GoToTab 1; SwitchToMode "normal"; }
        bind "Super 2" { GoToTab 2; SwitchToMode "normal"; }
        // ... Super 9까지 동일

        // 검색, 복사, 화면 지우기
        bind "Super f" { SwitchToMode "entersearch"; }
        bind "Super c" { Copy; }
        bind "Super k" { Clear; Write 10; }
    }
}

Cmd + D로 세로 분할, Cmd + Shift + D로 가로 분할. Cmd + F로 검색. Cmd + TCmd + N으로 새 탭. Cmd + [Cmd + ]으로 Pane 이동. Cmd + 1~Cmd + 9로 탭 전환. 전부 Ghostty를 쓸 때와 같습니다. 단축키를 새로 외울 필요가 없습니다.

clear-defaults=false는 Zellij의 기본 키 바인딩을 유지하면서 위에 덮어쓰겠다는 뜻입니다. 다만 일부 기본 키는 다른 도구와 충돌해서 풀어줬습니다.

        // Claude Code 등에서 쓰는 Ctrl + O, Ctrl + B와 충돌 방지
        unbind "Ctrl o"
        unbind "Ctrl b"
        unbind "Ctrl s"
        unbind "Ctrl q"

추가로 Ghostty는 마우스를 통해 분할된 탭 영역의 사이즈를 바꿀 수 있지만 Zellij는 그렇지 않기 때문에, 영역(Pane)의 크기를 편하게 바꿀 수 있도록 Alt + Shift + Arrow에 바인딩했습니다.

        bind "Alt Shift left" { Resize "Increase left"; }
        bind "Alt Shift down" { Resize "Increase down"; }
        bind "Alt Shift up" { Resize "Increase up"; }
        bind "Alt Shift right" { Resize "Increase right"; }

Alt 키 관련 참고 사항

macOS 터미널에서 Alt + Left/Right로 단어 단위 커서 이동을 쓰는 분이 많을 것입니다. Zellij의 기본 키 바인딩 중 Alt f가 이 시퀀스와 겹치는 문제가 있습니다(#3850). 그래서 Workaround로서 Alt f를 unbind하고 다시 bind해서 해결했습니다.

        unbind "Alt f"
        bind "Alt F" { ToggleFloatingPanes; }

키 바인딩 외 설정

하나의 세션 공유

// config.kdl
session_name "Main"
attach_to_session true
on_force_close "detach"

지금 제 설정은 모든 곳에서 하나의 세션을 공유하는 형태입니다. attach_to_session true로 설정하면 Zellij을 실행할 때 기존 세션이 있으면 그대로 붙습니다. 새 터미널 창을 열어도, 터미널을 껐다 켜도 작업 중이던 화면이 유지됩니다. on_force_close "detach"는 강제 종료 시에도 세션을 죽이지 않고 분리만 해줍니다.

이게 처음 Zellij를 도입한 이유이기도 합니다. 다른 곳에서 터미널을 열거나 SSH로 접속하면 같은 세션을 공유하고 이어서 작업할 수 있습니다. 연결을 끊고 자리를 비웠다가 돌아와도 그대로 잘 돌아가는 것을 확인할 수 있습니다.

zjstatus로 상단 바 꾸미기

Zellij에는 기본 상태 바가 있지만 좀 더 제 취향에 맞게 꾸미고 싶어서 zjstatus를 쓰고 있습니다. 별도 설치는 필요 없습니다. 레이아웃 파일에 wasm URL을 적어두면 Zellij이 처음 실행할 때 사용자에게 확인을 받은 뒤, 알아서 받아옵니다.

// ~/.config/zellij/layouts/default.kdl

layout {
    default_tab_template {
        pane size=2 borderless=true {
            plugin location="https://github.com/dj95/zjstatus/releases/latest/download/zjstatus.wasm" {
                // 좌측: 모드 + 세션 이름
                format_left   "{mode} #[fg=#eceff4,bold]{session}"
                // 중앙: 탭 목록
                format_center "{tabs}"
                // 우측: Git 브랜치 + 시간
                format_right  "{command_git_branch} {datetime}"

                // 모드별 표시
                mode_normal  "#[bg=#2e3440,fg=#5e81ac,bold]  NORMAL "
                mode_locked  "#[bg=#5e81ac,fg=#2e3440,bold] LOCKED "
                mode_resize  "#[bg=#b48ead,fg=#2e3440,bold]⇋ RESIZE "

                // 탭 표시 — 활성 탭은 bold + italic
                tab_normal   "#[fg=#4c566a] {index} {name} "
                tab_active   "#[fg=#d8dee9,bold,italic] {index} {name} "

                // Git 브랜치 (10초 간격 갱신)
                command_git_branch_command  "git rev-parse --abbrev-ref HEAD"
                command_git_branch_format   "#[fg=#5e81ac,bold] {stdout}"
                command_git_branch_interval "10"

                // 시간
                datetime          "#[fg=#2e3440,bg=#d8dee9,bold] {format}"
                datetime_format   "%Y-%m-%d %H:%M"
                datetime_timezone "Asia/Seoul"
            }
        }

        children

        // 단축키가 익숙하지 않다면 하단에 단축키 가이드용 기본 상태 바를 추가할 수 있습니다.
        pane size=1 borderless=true {
            plugin location="zellij:status-bar";
        }
    }
}

상단 바에는 현재 모드, 세션 이름, 탭 목록, Git 브랜치, 시간이 표시됩니다. 현재 어떤 모드에 있는지, 어떤 브랜치에서 작업 중인지 한눈에 볼 수 있어서 좋습니다. 색상은 Nord 컬러 팔레트를 기준으로 지정했습니다.

지금 제 설정에서는 하단 Zellij 기본 상태 바를 제외했지만, Zellij의 단축키를 가이드해주는게 필요하다면 저렇게 남겨둘 수도 있습니다.

색상은 전부 Nord 팔레트를 따랐습니다. Ghostty에서 OneNord 테마를, zjstatus에서는 Nord 계열 색상 코드를 직접 지정해서 톤을 맞췄습니다.

아쉬운 점: Bell 알림

Zellij을 쓰면서 한 가지 아쉬운 부분은 터미널에서 Bell과 알림 지원이 불완전하다는 것입니다.

현재 활성화된 탭의 영역에서 발생하는 bell은 터미널 에뮬레이터까지 전달되지만, 비활성 탭이나 숨겨진 플로팅 영역에서 발생하는 bell은 해당 영역을 다시 볼 때까지 전달되지 않습니다(#3245). 이 동작에 대한 논의가 #4595에서 진행 중이고, OSC 99(데스크톱 알림 프로토콜) 지원도 아직 구현되지 않은 상태입니다(#3451).

코딩 에이전트 등 TUI를 사용하는 여러 경우에서 작업이 끝나도 알림을 받지 못하는 상황이 생길 수 있습니다.

terminal-notifier를 통한 우회

terminal-notifier를 활용하면 터미널에서 macOS 알림을 직접 보낼 수 있습니다.

brew install terminal-notifier

Claude Code를 사용한다면 Hook을 통해 필요한 시점에 terminal-notifier로 알림을 보내 이 문제를 우회할 수 있습니다. Codex의 경우 아직 Hook 기능을 지원하지 않는데, 최근 이 기능을 개발 중이라는 코멘트가 있습니다. (openai/codex#2109)

결론

어쩌다 보니 터미널 에뮬레이터에 종속되지 않는 환경이 만들어졌습니다. 저는 여전히 Ghostty를 정말 좋아하지만, 이 설정은 Alacritty나 WezTerm 등을 써도 그대로 쓸 수 있습니다. 하지만 그게 이 글의 요점은 아닙니다.

저의 터미널 화면
저의 터미널 화면
저의 터미널 화면(Split 사용)
저의 터미널 화면(Split 사용)

제가 말하고 싶은 건, 이 설정을 통해 Ghostty를 쓸 때와 전혀 다르지 않은 느낌으로 Zellij을 쓸 수 있고 더 이상 터미널 멀티플렉서를 어려워하지 않아도 된다는 것입니다. 탭 영역 분할, Tab, 검색, 탭 혹은 탭 내 영역 간 이동 전부 같은 손동작으로 됩니다. 거기에 세션 유지, floating pane, 커스터마이징 가능한 상태 바까지 얹어집니다. 머슬 메모리를 크게 바꾸지 않으면서도 터미널의 사용 경험이 크게 좋아졌습니다.

염산하

@[email protected] · Reply to Haze's post

@nebuleto 혹시 마우스로 드래그해서 내용 복사할 때, 좌우로 분할된 경우가 잘 처리되나요? 복사 기능을 자주 쓰는데 좌우 분할에서는 그게 깔끔하게 안되는 경우가 많아서요

염산하

@[email protected] · Reply to 염산하's post

  • 선충(C. elegans): 302개 뉴런 → 1986년 완성, 배선 거의 완전히 고정
  • 초파리: 13만 9천개 뉴런 → 2024년 완성, 주요 회로는 보존되나 세부 시냅스는 개체마다 다름
  • 복잡한 생물일수록 ”유전자로 고정된 배선“보다 ”경험이 만드는 배선“의 비중이 커짐

🧩 더 복잡한 동물은?

  • 제브라피쉬(18만 뉴런): 2025년 부분 완성
  • 생쥐 시각피질 1㎣: 2025년 5억 시냅스 지도 완성
  • 생쥐 전뇌: 진행 중 (10~15년 예상)
  • 인간 전뇌: fMRI 수준만 완성, 시냅스 단위 전체 지도는 현재 기술로 불가

💡 핵심 질문 하나 ”뇌를 완전히 복사하면, 그게 그 생명체인가?“ 지금 초파리 한 마리가 그 질문을 처음으로 현실 위에 올려놓았다.


염산하

@[email protected]

오늘 나눈 대화를 SNS용으로 요약해드립니다! 🧠


🪰 초파리 한 마리가 디지털로 부활했다

과학자들이 초파리의 뇌(13만 9천 뉴런 · 5450만 시냅스)를 통째로 스캔해 컴퓨터에 ’복붙‘했더니, 디지털 초파리가 앞발을 비비고 먹이를 찾아 헤매기 시작했다.

🔬 어떻게 된 일? ① 2024년 — FlyWire 컨소시엄이 초파리 뇌 배선 지도(커넥톰) 완성. Nature 특별호 게재 ② 2026년 3월 — 스타트업 Eon Systems가 그 배선에 가상 신체를 붙여 행동하는 디지털 파리 시연

🎯 Eon Systems의 목표? 파리 → 생쥐(7천만 뉴런) → 인간 뇌(860억 뉴런) 순서로 에뮬레이션 스케일업. ”2030년 인간 뇌 에뮬레이션“이 최종 목표. 아직 정식 논문은 없고 시연 영상으로만 공개된 상태.

🐛 왜 초파리가 특별한가?

永瀬_IP23🎗️'s avatar
永瀬_IP23🎗️

@[email protected]

지금 대통령이 담합 때려잡는것만 봐도 알 수 있듯, 결국은 정부가 움직여야 거대자본이나 기술과 맞설 수 있음. 규제라는 칼이 없이는 개인을 지킬 수 없다. 그렇기 때문에 민주정 하에서는 개별 시민의 의식이 중요하고, 이를 위해 문제를 알리고 서로를 지지하며 '결과적으로' 정부를 움직여야 한다고 생각한다.

신간 IT서적 알림봇's avatar
신간 IT서적 알림봇

@[email protected]

지속 가능한 프런트엔드 엔지니어링 - 유지보수하기 쉽고 확장성 있는 프런트엔드 구축을 위한 핵심 원칙과 기법 (이문기 (지은이) / 위키북스 / 2026-02-26 / 28,000원) feed.kodingwarrior.dev/r/uD5CGE

aladin.co.kr/shop/wproduct.asp

염산하

@[email protected] · Reply to Woojin Kim's post

@me -_-;

염산하

@[email protected]

더 송라이터스

염산하

@[email protected]

도시를 만드는 기술 이야기

보리's avatar
보리

@[email protected]

지금까지 저와 주변 사람들을 모니터링하고 관찰하고 지레짐작한 결과, 안 읽을 책을 쌓아놓는 사람들은 사실은 안 살 게임도 쌓아놓고 안 쓸 잉크도 쌓아놓고 (…잉크를 쌓을 수가 있나?) 안 먹을 음식도 냉동실에 쌓아두고 인터넷 즐겨찾기도 SNS의 북마크함도 잔뜩 쌓아놓는 그런 성향이 있는 것 같다는 생각을 하곤 해요.

Simon Willison's avatar
Simon Willison

@[email protected]

Wrote up my first impressions of Claude Cowork, Anthropic's new general purpose agent released today for $100+/month subscribers as part of their macOS desktop app simonwillison.net/2026/Jan/12/

한겨레's avatar
한겨레

@[email protected]

미네소타주 미니애폴리스에서 이민단속반이 한 여성을 총으로 쏜 뒤 “정당방위”라고 주장한 데 대해 도시 전체가 분노로 들끓고 있습니다. 총격 순간을 포착한 영상 속 모습은 테러와는 다소 거리가 있어 보이는 까닭입니다.

분노의 미니애폴리스…6살 아이 엄마 사살한 이민단속반에...

염산하

@[email protected]

소프트웨어 개발팀에는 파인다이닝 팀이 어울리는 비유인가?

염산하

@[email protected]

갑자기 "이븐하게 익지 않았어요"가 떠올라 버림 ㅠㅠ https://mastodon.social/@kepano/115831704433058850

洪 民憙 (Hong Minhee) :nonbinary:'s avatar
洪 民憙 (Hong Minhee) :nonbinary:

@[email protected] · Reply to 洪 民憙 (Hong Minhee) :nonbinary:'s post

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.

An illustration shows the mascots of several fediverse projects happily gathered in a sunny, green meadow with rolling hills and wildflowers. In the center, the blue dinosaur mascot of Fedify holds a paper with a colorful network diagram in its mouth, smiling. To the left, the yellow elephant mascot of Mastodon walks playfully with the small white mouse mascot of Lemmy perched on its trunk. To the right, the anime-style girl mascot of Misskey, wearing a sailor uniform and cat ears, kneels and smiles, with the white Among Us-like wizard mascot of Akkoma next to her. The overall mood is cheerful and friendly.
ALT text detailsAn illustration shows the mascots of several fediverse projects happily gathered in a sunny, green meadow with rolling hills and wildflowers. In the center, the blue dinosaur mascot of Fedify holds a paper with a colorful network diagram in its mouth, smiling. To the left, the yellow elephant mascot of Mastodon walks playfully with the small white mouse mascot of Lemmy perched on its trunk. To the right, the anime-style girl mascot of Misskey, wearing a sailor uniform and cat ears, kneels and smiles, with the white Among Us-like wizard mascot of Akkoma next to her. The overall mood is cheerful and friendly.
염산하

@[email protected] · Reply to Woojin Kim's post

@me systemd 유닛 설정으로 어떻게 되지 않을까요? 죽으면 살려주는 거 있을텐데요...

염산하

@[email protected]

손종원 좀 예술. 파인다이닝의 역량인가? 기획력과 구현력 최상급에 위기대처 능력까지. 서포트에 임할 때도 앞을 내다보는 서포터... (편집의 힘도 없진 않겠지만) 개인전 팀전 다 수준급이라니.

흑백요리사2

자손킴's avatar
자손킴

@[email protected]

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가 할당되지 않은 상태이기 때문에 브로트캐스트를 통해 정보를 주고 받는다.

  1. DHCP Discover: 클라이언트가 네트워크에 접속하면 DHCP 서버를 찾기 위해 브로드캐스트로 Discover 메시지를 전송한다. 이 시점에서 클라이언트는 아직 IP 주소가 없으므로 출발지 IP는 0.0.0.0으로 설정된다.
  2. DHCP Offer: DHCP 서버가 Discover 메시지를 수신하면 할당 가능한 IP 주소와 서브넷 마스크, 임대 시간 등의 설정 정보를 담아 Offer 메시지를 유니캐스트로 응답한다. 네트워크에 여러 DHCP 서버가 존재하는 경우 클라이언트는 복수의 Offer를 수신할 수 있다.
  3. DHCP Request: 클라이언트가 수신한 Offer 중 하나를 선택하여 해당 DHCP 서버에 IP 주소 할당을 정식으로 요청한다. 이 메시지도 브로드캐스트로 전송되며, 선택되지 않은 다른 DHCP 서버들에게 해당 Offer가 거절되었음을 알리는 역할도 한다.
  4. DHCP ACK: DHCP 서버가 Request를 승인하면 ACK 메시지를 전송하여 IP 주소 할당을 확정한다. 클라이언트는 이 메시지를 수신한 후 비로소 할당받은 IP 주소를 사용할 수 있게 된다. 만약 요청한 IP 주소를 할당할 수 없는 경우에는 DHCP NAK 메시지가 전송된다.
  5. 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 부팅의 동작 흐름은 다음과 같다.

  1. 클라이언트가 전원을 켜면 NIC의 PXE 펌웨어가 DHCP Discover 메시지를 브로드캐스트한다.
  2. DHCP 서버는 IP 주소와 함께 옵션 66(TFTP 서버 주소)과 옵션 67(부트 파일 경로)을 응답한다.
  3. 클라이언트는 TFTP 프로토콜을 사용하여 지정된 서버에서 부트 파일(예: pxelinux.0, bootx64.efi)을 다운로드한다.
  4. 다운로드한 부트 로더가 실행되어 OS 설치 또는 부팅이 진행된다.

이처럼 DHCP의 옵션 필드를 활용하면 단순한 IP 할당을 넘어 네트워크 인프라 자동화의 기반을 구축할 수 있다.

洪 民憙 (Hong Minhee) :nonbinary:'s avatar
洪 民憙 (Hong Minhee) :nonbinary:

@[email protected]

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.

A photograph showing a giant, animated blue cartoon brachiosaurus standing next to a real-world brick university building with large glass windows and the letters “ULB” on the façade. The friendly-looking dinosaur holds a white paper with a colorful network diagram in its mouth. Parked cars and a street are in the foreground under an overcast sky.
ALT text detailsA photograph showing a giant, animated blue cartoon brachiosaurus standing next to a real-world brick university building with large glass windows and the letters “ULB” on the façade. The friendly-looking dinosaur holds a white paper with a colorful network diagram in its mouth. Parked cars and a street are in the foreground under an overcast sky.
염산하

@[email protected]

일탈의 정상화

매일 업무에서 수많은 선택을 합니다. 데이터를 상부에 보고할 것인가, 이 정도 오차는 무시할 것인가 등이죠. 이런 선택 과정에서 더이상 문제라고 생각하지 않게 되는 작은 일탈들이 쌓여 전체적인 조직 문화를 형성합니다. 챌린저호 사고는 강력한 냉전을 배경으로 한 체제 경쟁의 압력이 크게 작용했다. 압력이 강하게 내려오면서 실무자들의 안전을 타협하는 문화가 고착된 것.

정상사고의 함정

1979년 스리마일 원전 사고 이후 미국 사회학자 찰스 페로가 제시한 이론. 핵발전소, 로켓 등 고도로 복잡하고 긴밀하게 연결된 시스템에서는 아무리 철저히 관리해도 사소한 실수나 예측 불가능한 상호작용으로 사고가 ‘정상적‘으로 즉, 피할 수 없이 발생한다.

과학동아 26년 1월호, 챌린저호 참사 40주기, 참사가 남긴 질문 중

자손킴's avatar
자손킴

@[email protected]

다음에 6장이 있지만 총정리를 하는 챕터이기 때문에 실질적인 내용은 여기까지가 끝이다. 책에서는 SSL 오프로드만 다루고 끝나지만 내부망 안에서의 보안도 짧게 정리했다.

TLS(SSL) 오프로드

TLS를 사용하면 보안이 강화되지만 TLS 핸드셰이크와 암복호화 작업에 CPU 자원을 많이 소모하게 된다. 이것을 전용 장비에 맡기면 웹서버는 효율적으로 애플리케이션 로직 처리에만 신경 쓰면 된다. 특히 로드밸런서에서 중앙집중식으로 TLS를 처리하면 웹서버의 부하가 크게 줄어들고 요청을 빠르게 처리할 수 있게 되어 시스템 전반적으로 부하 분산의 효과가 커진다. 특히 관리가 까다로운 공인 인증서를 로드밸런서에서 집중 관리할 수 있어 운영 부담이 크게 줄어든다.

TLS 오프로드 동작방식

  1. 클라이언트의 HTTPS 요청: 클라이언트가 웹 서비스에 접속하기 위해 HTTPS 요청을 보낸다. 이 시점에서 클라이언트는 TLS 핸드셰이크를 시작한다.

  2. 방화벽(fw1) 통과: 요청이 방화벽 fw1에 도달한다. 방화벽은 허용된 포트(443)로 들어오는 트래픽인지 확인하고, 패킷 필터링 규칙에 따라 트래픽을 통과시킨다. 이 단계에서 트래픽은 여전히 암호화된 상태이므로 일반적인 방화벽은 패킷의 내용을 검사할 수 없고 IP/포트 기반 필터링만 수행한다. (다만 SSL 인스펙션 기능이 있는 차세대 방화벽(NGFW)은 여기서 복호화 후 검사를 수행하기도 한다.)

  3. 로드밸런서(lb1)에서 TLS 종료: 로드밸런서 lb1이 클라이언트와 TLS 핸드셰이크를 완료한다. 서버 인증서를 클라이언트에게 제시하고 세션 키를 협상한 뒤 암호화된 트래픽을 복호화한다. 이 과정을 SSL/TLS Termination이라고 부른다.

  4. 평문 HTTP로 백엔드 전달: 로드밸런서는 복호화된 요청을 분석하여 라우팅 규칙에 따라 백엔드 서버 sv1 또는 sv2에 평문 HTTP로 전달한다. 이때 로드밸런서는 X-Forwarded-For, X-Forwarded-Proto 같은 헤더를 추가하여 원본 클라이언트 정보와 프로토콜 정보를 백엔드에 전달할 수 있다.

  5. 백엔드 서버 처리: 웹서버 sv1 또는 sv2는 평문 HTTP 요청을 받아 애플리케이션 로직을 처리하고 응답을 생성한다. 암복호화 작업이 없으므로 CPU 자원을 온전히 비즈니스 로직에 사용할 수 있다.

  6. 응답 암호화 및 전송: 백엔드 서버의 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)'s avatar
洪 民憙 (Hong Minhee)

@[email protected] · Reply to 洪 民憙 (Hong Minhee)'s post

오늘은 OpenCode에서 공짜로 제공하길래 MiniMax M2.1로 코딩을 좀 해봤다. 몇 시간 정도 해본 느낌으로는 GLM-4.7보다는 훨씬 나았고, 체감상으로는 대충 Claude Sonnet 4와 비슷한 정도로 말귀를 잘 알아듣는 느낌이었다. 컨텍스트 윈도가 긴 것도 장점이었다. 다만, 컨텍스트가 좀 길어지니까 끝도 없이 삽질을 반복하게 되어서, 그 쯤에서 모델을 GPT-5.1 Codex Max로 바꿔서 진행했다. GPT-5.1 Codex Max로 삽질 구간 벗어난 뒤에 금방 다시 MiniMax M2.1로 돌아와서 계속 코딩을 했고, 전반적으로 싼 값을 감안하면 굉장히 좋다고 느꼈다.

요즘에는 평소에 Claude Opus 4.5를 주력으로 사용하니까, 아무래도 비교가 될 수밖에 없었는데:

  • 역시나 눈치라고 해야 하나, 센스는 떨어진다. Claude Sonnet 4.5보다도 떨어지는 듯. 이를테면 Markdown 문서를 수정하도록 지시하면 기존의 일관성 있게 잘 짜여 있던 문서 서식이 금방 무너지는 게 느껴진다.
  • AGENTS.md의 세세한 지시를 좀 뭉개는 느낌이 있다. 예를 들면 TypeScript 코딩할 때 any 타입을 쓰지 말라고 했음에도 무시하고 사용한다든가. Claude 계열 모델들에서는 이런 건 잘 못 겪는다.
  • 작업의 맥락보다 이미 학습되어 있는 자신의 지식을 더 따르는 느낌이 있다. 이를테면 일부러 여러 JavaScript 런타임에서 두루 돌아가게 하려고 Deno API를 안 쓰고 Node.js API를 써서 짜 둔 코드베이스에서 갑자기 Deno API를 꺼내서 쓰기 시작하는 식이다. 이것도 눈치 문제로 볼 수도 있을 듯.
  • 그렇게 중요하진 않지만 자연어 응답에 언어가 조금 섞인다. 특히 국한문혼용체가 종종 나온다. 나로서는 오히려 좋다(?). 그런데 자세히 보면 대륙에서 쓰는 간화자가 아니라서, 중국어가 섞이는 건 아닌 것 같다. 아마도 일본어 아니면 대만/홍콩의 중국어가 섞이는 것 같다. 아니면 정말로 국한문혼용체일지도? 그리고 아랍어도 한 번 섞이는 걸 봤다.
  • 속도는 그냥저냥 쓸만하지만 딱히 빠른 것도 아닌데, 이건 OpenCode에서 공짜로 제공하는 걸 써서 그럴 수도 있다. Claude Opus 4.5보다는 약간 느리다고 느꼈지만, 이것도 그냥 체감이라 정확하진 않다. 삽질하는 걸 더 많이 봐서 느리다고 착각한 걸 수도 있고.

일단은 OpenCode에서 공짜로 제공하는 동안은 좀 더 써 볼 생각이다. 돈 내고 쓸 생각이 있냐 하면, 그건 좀 고민이 된다. 코딩 요금제를 보면 5시간에 300 프롬프트짜리가 월 20불 정도 된다. 지금은 Claude Max 요금제를 쓰고 있는데, 아무래도 부담이 좀 되긴 해서, Claude Pro로 내리고 MiniMax를 섞어서 쓰면 어떨까 생각만 해보고 있다.

소피아's avatar
소피아

@[email protected]

X-Frame-Options 의 악몽에서 깨어나세요, 프록시 서버 개발기

X-Frame-Options? 그게 뭔가요?

우리가 아는 몇몇 대형 웹 서비스들(유튜브 등)은 보통의 경우 다른 웹 사이트에서 iframe 요소를 통해 임베딩 되는 것을 거부하지 않습니다. 다만, 몇몇 웹 서비스는 다른 웹 페이지에서 iframe 요소를 통해 표시되길 거부합니다. 제품 정책 및 보안상의 이유로 표시를 거부하는 목적이 있겠습니다.

이러한 니즈를 충족시킬 수 있는 HTTP 헤더가 X-Frame-Options 입니다. 이 헤더의 값을 SAMEORIGIN 내지는 DENY로 설정하면, 직관적인 값에 따라 알맞게 프레임 내 임베딩 가능 여부를 결정할 수 있습니다.

네이버 웹 사이트의 Response Header 목록, X-Frame-Options가 명시되어 있다.

위 이미지에서 볼 수 있듯, 네이버는 X-Frame-Options 헤더를 명시함으로써 그 어떠한 출처에서도 프레임 내에 임베딩 되는 것을 거부한 상황입니다. #

왜 이걸 우회하나요?

image

웹 페이지 내에 다른 웹 페이지가 임베딩 되어 '미리보기' 처럼 제공되는 경험을 보신 적이 있으신가요? Omakase AI는 자사 프로덕트의 데모를 위와 같이 제공하고 있습니다. 캡쳐하여 실시간으로 전송되는 영상의 화면 위에, 자사 컨텐츠를 올려두어 실제 적용 시에 어떤 네러티브를 제공할지 데모 형식으로 보여줍니다.

문제는 이 모든 경험이 영상을 통해 진행 된다는 점 입니다. 영상 전송은 필연적으로 지연 시간이 존재할 수 밖에 없습니다. 여러분이 스크롤을 내리고, 클릭을 하는 등의 작은 인터렉션 하나가, 큰 지연 시간 뒤에 처리가 된다고 하면 유저는 답답하고 매끄럽지 않음을 느낄 것이고 이는 곧 이탈로 이어질 가능성이 있습니다. (구글은 비슷한 맥락에서 Core Web Vitals로써 INP를 설명하고 있습니다)

따라서 저는 영상을 보내는 나이브한 방법 이외의 유저의 브라우저에서 외부 웹 서비스를 표시할 좋은 방법을 찾아야 했고, 그것이 바로 프레임 내지는 iframe을 사용하는 방법 이었습니다. 이런 맥락에서 X-Frame-Options를 우회할 필요가 생긴 것 입니다.

어떻게 했나요?

중간자의 필요성

image

우리는 정상적인 방법으로는 제 3자 출처의 요청을 가로채어 응답 데이터 및 헤더를 조작할 수 없음을 잘 알고 있습니다. 여기서 필요한게 중간자 (Man in the Middle) 입니다. 누군가를 클라이언트 - 원격지 서버 사이에 두어, 서로에게 오가는 요청과 응답을 수정하는 작업을 수행하도록 하는 것 입니다. 그렇게 하면, 둘은 각자 수신한 요청과 응답이 모두 원본인지, 수정한 것인지 알 수 있는 방법은 거의 없을 것 입니다.[1]

image

이 중간자 역할을 하는 프록시 서버를 중간에 두어 요청을 모두 프록시 서버를 거치도록 하는 방법을 이용하는 것 입니다. 간단하게는 X-Frame-Options 응답 헤더의 제거가 있을 것 입니다. 중간자가 X-Frame-Options 헤더를 제거함으로 응답을 수신하는 클라이언트 브라우저의 iframe 요소는 큰 문제 없이 내용을 표시할 수 있게 됩니다.

여기서 끝이 아니다. URL을 조작하기

처음에는 그저 GET Query Parameter로 프록시 서버에게 어떤 원격 URL을 프록시 할 것인지 명시하도록 구현 했습니다. 예를 들면 다음과 같은 형식이 될 수 있겠습니다:

https://example.com/proxy?target=https://www.naver.com/...

보통의 경우에는 별 문제 없이 동작 했습니다만, 재앙은 그다지 먼 곳에 있지 않았습니다. 만약 다음과 같은 코드가 원격지 웹 서비스 코드에 있다고 해봅시다. 아래 코드는 무신사 웹 페이지의 빌드된 소스코드 입니다:

import {E as k0} from "./vendor/react-error-boundary.js";
import {b as o0, d as Wt} from "./vendor/react-router.js";
import {d as F0} from "./vendor/dayjs.js";
import {L as Qt} from "./vendor/lottie.js";
import "./vendor/scheduler.js";
import "./vendor/prop-types.js";
import "./vendor/react-fast-compare.js";
import "./vendor/invariant.js";
import "./vendor/shallowequal.js";
import "./vendor/@remix-run.js";
import "./vendor/tslib.js";
import "./vendor/@emotion.js";
import "./vendor/stylis.js";
import "./vendor/framer-motion.js";
import "./vendor/motion-utils.js";
import "./vendor/motion-dom.js";

무신사는 내부적으로 ESM을 사용해서, import 구문을 통해 필요한 에셋을 불러오는 코드를 사용중에 있습니다. 문제는 여기서 발생합니다. import 의 대상이 되는 소스코드가 상대 경로를 따르게 되어 아래와 같은 결과를 초래하게 됩니다.

https://example.com/proxy?target=https://www.naver.com/...

위와 같은 URL을 표시하고 있는 `iframe` 요소에서,
`import "./vendor/framer-motion.js"` 구문을 만난다면..

https://example.com/proxy/vendor/framer-motion.js 를 요청하게 됨.

이를 해결하기 위해, 프록시 된 대상 URL에 대한 개념의 도입이 필요 했습니다. 상대 경로 진입에도 안정적으로 작동할 수 있는 새로운 방식의 접근이 필요 했습니다. 저는

  1. 상대 경로 접근에도 안전하며,
  2. 원본 URL의 구조를 그대로 남길 수 있는 구조

를 생각해 냈어야 했고, 그 결과는 이렇습니다. https://section.blog.naver.com/BlogHome.naver?directoryNo=0&currentPage=1&groupId=0 를 예시로 들면, 프록시화 (Proxified) 된 URL은 다음과 같은 것 입니다.

https://example.com/proxy/section/blog/naver/com/_/BlogHome.naver?...

URL hostname. 구분자를 /로 치환하고, 이후의 모든 pathname, search 등은 모두 _ 구분자 뒤로 넘김으로서 URL의 원형을 유지할 수 있게 됩니다. 추가적으로 상대 경로 접근에도 안전한 URL을 만들 수 있습니다.

최종 보스가 남아있다. HTML API 후킹하기

우리는 상대 경로 문제를 해결하기 위해 URL을 프록시화 하는 방법을 사용했고, 이는 제대로 동작하는 듯 해보였습니다. 악몽은 React, Vue 등의 SPA 웹 앱을 프록시하여 표시하는 데에서 시작 되었습니다.

React, Vue 와 같은 프레임워크들은 History API 및 window.location 객체를 기반으로 한 Routing 기능을 제공하고 있습니다.[2] 이 말은, 결국엔 어떤 프레임워크가 되었든 저수준 빌트인 자바스크립트 API를 사용할 수 밖에 없다는 것을 의미 합니다. 그렇다면 직관적으로 생각 해봤을 때,

window (및 globalThis) 객체의 location 속성의 값을 변경해주면 되지 않겠나?

라고 생각할 수 있습니다. 그러나 이는 불가능 합니다.

image

어떠한 이유 때문인지는 알 길이 없었지만, 자바스크립트는 그렇게 만만한 존재가 아니었습니다. 다른 좋은 방법을 찾아야 할 필요가 있었고, 결론에 도달하는 데에는 오랜 시간이 걸리지 않았습니다. 그것은 바로 원격지 웹 페이지에서 실행되는 모든 스크립트의 window.location 객체 접근을 감시하면 어떨지에 대한 아이디어 였습니다.

번들된 소스코드의 경우 대체적으로 다음과 같은 형식을 가지게 됩니다:

const l = window.location;
/* ... */ l.pathname /* ... */

여기서 우리는 변수 lwindow.location의 별칭인지 소스코드만 분석해서는 알기 매우 어렵습니다. 따라서, babel을 사용해서 소스코드를 AST로 분석하고, 모든 프로퍼티 접근을 특정 함수 호출로 변환하면, '특정 함수'에서 모든 것을 처리할 수 있으니 좋을 것 같다는 생각이 있었고, 실행에 옮겼습니다:

const l = window.location;
l.pathname;

// 위 코드는 아래와 같이 변환됨

const l = __internal_get__(window, 'location');
__internal_get__(l, 'pathname');

__internal_get__ 함수 내부에서 첫번째 인자가 window.location 과 동일한 인스턴스를 가지고 있는지 비교하거나, 두번째 인자인 프로퍼티 키를 비교해서 href 등의 값이라면, 원하는 값을 반환하도록 후킹 함수를 만들 수 있겠습니다.

function __internal_get__(owner, propertyKey) {
  if (owner === window.location) {
    return {
      get href() { /* proxified 된 url을 기반으로 Router를 속이는 URL을 반환하는 로직 */ }
    }
  }

  // ...
}

마치며

글에 열거한 내용 이외에도 정말 많은 기술이 사용 되었는데, 아주 재밌는 경험 이었습니다. 혹여나 이러한 비슷한 기능을 하는 기능을 개발할 일이 있으시다면, 도움이 됐으면 좋겠습니다.


  1. 여기서 거의 라는 표현을 사용한 이유는, 제 짧은 식견에서 보자면 비슷한 맥락에서 사용하는 기법으로 integrity 속성이 있을 수 있겠습니다. ↩︎

  2. 예를 들면, location.pathname을 읽어 현재 Route가 어떤 Route인지 감지하는 등의 동작이 있겠음. ↩︎

Older →