Web

378 posts
_redirects: 정적 사이트에서 리디렉션 규칙 관리하기

_redirects: 정적 사이트에서 리디렉션 규칙 관리하기

블로그 글의 주소를 바꾸거나 문서 사이트의 폴더 구조를 정리하고 나면 꼭 이런 일이 생기는데요. 예전에 SNS나 다른 블로그에 공유된 옛 주소로 들어온 사람들이 모두 404 페이지를 보게 됩니다. 😅 게다가 검색 엔진이 옛 주소에 쌓아 둔 신호도 새 주소로 넘어가지 못합니다. 이럴 때 필요한 게 리디렉션(redirect) 규칙입니다. 서버를 직접 운영한다면 서버 코드에서 처리하면 되지만, 정적 사이트는 이야기가 다른데요. 이 글에서는 정적 사이트에서 리디렉션 규칙을 어디에 어떻게 적는지, 호스팅 플랫폼별 문법과 흔히 빠지는 함정을

SvelteKit 3: Svelte로 풀스택 웹 앱 만들기

SvelteKit 3: Svelte로 풀스택 웹 앱 만들기

Svelte 5 글에서 룬으로 컴포넌트를 만들어 봤는데요. 컴포넌트만으로는 웹 앱이 완성되지 않습니다. 주소마다 다른 페이지를 보여 줘야 하고, 서버에서 데이터를 가져와야 하고, 폼을 처리하고, 어딘가에 배포도 해야 하죠. 🤔 이런 일을 모두 맡아 주는 것이 Svelte 팀이 직접 만드는 SvelteKit입니다. 마침 2026년 10월 1일에 SvelteKit 3가 정식으로 출시됐는데요. 설정 파일이 바뀌고 $lib 별칭이 #lib로 바뀌는 등 프로젝트 구조부터 달라졌습니다. 이번 글에서는 SvelteKit 3로 작은 블로그 앱을

TSRX: JSX의 뒤를 잇는 TypeScript UI 언어 확장

TSRX: JSX의 뒤를 잇는 TypeScript UI 언어 확장

React 컴포넌트를 쓰다 보면 이런 코드를 한 번쯤 마주치게 되는데요. 로딩, 에러, 목록, 빈 상태를 구분하는 평범한 요구사항인데 정작 코드는 괄호를 세 겹 열고 삼항 연산자를 두 번 중첩합니다. 자바스크립트에는 if도 있고 for도 있는데, JSX 안에서는 이 문법들을 쓸 수 없어서 전부 표현식으로 바꿔 끼워 넣어야 하기 때문입니다. 😅 TSRX는 바로 이 지점을 건드리는 프로젝트입니다. 이번 포스팅에서는 TSRX가 어떤 문법을 제공하고, 왜 이런 문법이 지금 나왔는지 살펴보겠습니다. TSRX는 무엇인가요? TSRX는 Typ

Bun 1.4 출시: Zig에서 Rust로 다시 쓴 자바스크립트 런타임

Bun 1.4 출시: Zig에서 Rust로 다시 쓴 자바스크립트 런타임

2026년 8월 20일에 Bun 1.4가 출시되었습니다. 저는 릴리스 노트를 열어보고 잠깐 스크롤을 멈췄는데요. 보통 이런 글은 새 API 목록으로 시작하는데, 이번에는 첫 문단부터 "Bun을 Zig에서 Rust로 다시 썼다"는 문장이 나오더라고요 🤯 런타임의 구현 언어를 통째로 바꾼다는 건 자동차가 달리는 중에 엔진을 갈아 끼우는 일에 가깝습니다. 그런데 이번 릴리스는 그 소식 말고도 볼 게 많습니다. Node.js 호환성 테스트가 1,517개 늘었고, 이미지 처리와 크론, 마크다운 파서가 표준 라이브러리로 들어왔으며, bun

AT Protocol: Bluesky를 떠받치는 탈중앙 소셜 프로토콜

AT Protocol: Bluesky를 떠받치는 탈중앙 소셜 프로토콜

소셜 서비스를 옮겨본 적 있으신가요? 계정을 새로 파고, 팔로워를 처음부터 다시 모으고, 그동안 쓴 글은 옛 서비스에 그대로 남겨두고 나오게 됩니다. 계정도 데이터도 인맥도 전부 그 회사 서버 안에 있으니 당연한 일이죠. 서비스가 마음에 안 들어도 쉽게 못 떠나는 이유이기도 하고요. 😅 AT Protocol(atproto)은 이 구조를 뒤집어보려는 시도입니다. Bluesky를 떠받치는 프로토콜로 알려져 있지만, 정확히 말하면 Bluesky는 이 프로토콜 위에 올라간 여러 앱 중 하나일 뿐입니다. 신원과 데이터를 앱 바깥으로 꺼내

어색하게 끊기는 한국어 줄바꿈 다듬는 법

어색하게 끊기는 한국어 줄바꿈 다듬는 법

공들여 쓴 글에 제목을 달고 모바일에서 열어봤는데, 마지막 줄에 짧은 단어 하나만 덩그러니 남아 있는 걸 보신 적 있으신가요? 😅 글꼴도 크기도 잘 골랐는데 제목이 어딘가 엉성해 보이는데요. 한국어라면 여기에 더해 "브라우저"가 "브라" / "우저"로 뚝 잘리는 일까지 생깁니다. 이런 문제는 글꼴이나 여백을 아무리 만져도 해결되지 않습니다. 줄을 어디서 나눌지는 브라우저의 줄바꿈 알고리즘이 정하는 영역이기 때문인데요. 우리가 개입하려면 그 알고리즘에 직접 말을 걸어야 합니다. 이번 포스팅에서는 word-break 속성과 text-

TanStack Markdown과 Highlight

TanStack Markdown과 Highlight

문서 사이트를 만들다 보면 이상한 순간이 옵니다. 정작 보여주려는 건 글자 몇 천 개인데, 그걸 그리려고 내려받는 자바스크립트가 1MB를 넘어가는 거죠. 😅 TanStack 팀이 딱 이 상황이었습니다. tanstack.com의 문서 페이지 하나가 스크립트만 약 1.1MiB를 전송하고 있었는데, 그중 358KiB 정도가 오직 코드 구문 강조에 쓰이고 있었어요. 이걸 React Server Components로 감추는 대신, 팀은 도구 자체를 작게 만드는 쪽을 택했습니다. 그렇게 나온 게 TanStack Markdown과 TanSta

Web Awesome으로 프레임워크 독립적인 UI 만들기

Web Awesome으로 프레임워크 독립적인 UI 만들기

관리자 화면에 버튼 하나를 추가하려는데 디자인부터 다시 잡아야 했던 적이 있나요? 버튼만 끝나면 다행이지만 입력창, 대화 상자, 탭, 드롭다운까지 필요해지면 이야기가 달라집니다. 직접 만들자니 접근성과 키보드 조작이 걱정되고, 익숙한 UI 프레임워크를 쓰자니 특정 JavaScript 프레임워크에 프로젝트 전체가 묶일 수 있죠. Web Awesome은 이 사이를 파고드는 UI 컴포넌트 라이브러리입니다. React나 Vue 전용 컴포넌트 대신 브라우저 표준인 웹 컴포넌트(Web Components)로 만들어졌는데요. 일반 HTML 페이

SSRF(Server-Side Request Forgery) 공격과 방어

SSRF(Server-Side Request Forgery) 공격과 방어

요즘 웹 서비스에는 서버가 사용자 대신 외부 URL로 요청을 보내는 기능이 흔합니다. 링크를 붙여넣으면 미리보기 카드를 만들어 주고, 이미지 주소를 넣으면 썸네일을 대신 받아오고, 웹훅 주소를 등록해 두면 이벤트가 생길 때마다 서버가 그 주소로 알림을 쏴 줍니다. 편리하죠. 그런데 "사용자가 건넨 주소로 서버가 대신 요청을 보낸다"는 이 평범한 동작이, 잘못 다루면 서버를 통째로 공격자의 손발로 내주는 통로가 됩니다. 이번 글에서 다룰 SSRF(Server-Side Request Forgery) 이야기입니다. 이름부터 CSRF와 헷

Discord 게이트웨이로 실시간 이벤트 받기

Discord 게이트웨이로 실시간 이벤트 받기

봇 개발을 하다 보면 결국 마주치는 통로가 **게이트웨이(Gateway)**입니다. 슬래시 커맨드를 만들 때 client.login() 한 줄로 봇을 온라인으로 띄웠는데, 그 순간 봇은 바로 이 게이트웨이로 디스코드와 연결을 맺은 거였어요. 이번 글에서는 그 통로의 안쪽을 들여다보며, 봇이 어떻게 서버의 모든 일을 실시간으로 지켜보는지, 그리고 그렇게 만든 봇을 어떻게 24시간 운영하는지까지 살펴봅니다. 게이트웨이는 항상 열려 있는 WebSocket REST API가 "내가 물어보면 답해주는" 요청-응답 방식이었다면, 게이트웨이는

실시간 단방향 통신을 위한 Server-Sent Events(SSE)

실시간 단방향 통신을 위한 Server-Sent Events(SSE)

ChatGPT에게 질문을 던지면 답변이 한 글자씩 타이핑되듯 흘러나오는 모습, 다들 익숙하시죠? 증권 앱의 시세가 새로고침 없이 실시간으로 바뀌거나, 웹 대시보드에 알림이 띵 하고 도착하는 것도 마찬가지인데요. 이런 화면들의 공통점은 서버에서 새로운 데이터가 생길 때마다 클라이언트가 곧바로 받아본다는 것입니다. 실시간 양방향 통신을 위한 웹소켓(WebSocket) 글에서 우리는 HTTP의 한계와 이를 극복하는 웹소켓을 살펴봤는데요. 그런데 위 예시를 가만히 보면 한 가지 공통점이 더 있습니다. 데이터가 서버에서 클라이언트로 한 방향

Satteri: Rust로 파싱하고 JavaScript로 확장하는 마크다운 처리기

Satteri: Rust로 파싱하고 JavaScript로 확장하는 마크다운 처리기

블로그나 문서 사이트를 운영하다 보면 빌드 시간이 슬금슬금 늘어나는 순간이 옵니다. 글이 수백 개를 넘어가면 마크다운 파싱과 변환에만 수십 초가 쓰이고, 거기에 remark/rehype 플러그인을 몇 개 더 끼우면 1분을 훌쩍 넘기는 경우도 흔하죠. 저도 이 블로그를 운영하면서 빌드 로그를 보다가 "마크다운 처리에 이만큼 시간을 쓴다고?"라는 생각을 한두 번 해본 게 아닌데요 😅 최근 Astro 6.4에서 markdown.processor API가 새로 등장하면서, unified 생태계가 아닌 다른 마크다운 처리기로 갈아탈 수 있

Void 폼과 액션: useForm으로 끝까지 타입 안전하게

Void 폼과 액션: useForm으로 끝까지 타입 안전하게

Void 라우팅에서 데이터를 읽어 오는 로더는 자세히 봤는데요. 정작 데이터를 바꾸는 쪽은 "로더 대신 action을 내보낸다"는 한 줄로 넘어갔습니다. 사실 폼을 다루는 일은 읽기보다 손이 많이 가죠. 입력값을 검증하고, 에러를 화면에 표시하고, 제출 중에는 버튼을 막고, 성공하면 목록을 새로고침하고… 이번 글에서는 Void가 이 과정을 useForm 하나로 어떻게 묶어주는지 살펴보겠습니다. 액션으로 데이터 바꾸기 데이터를 읽을 때 로더를 썼다면, 바꿀 때는 액션을 씁니다. 페이지의 .server.ts에서 action을 내보내면

React Doctor로 React 코드 건강 점검하고 코딩 에이전트 가르치기

React Doctor로 React 코드 건강 점검하고 코딩 에이전트 가르치기

코딩 에이전트와 함께 React 코드를 짜다 보면 묘한 위화감이 들 때가 있습니다. 코드는 분명히 동작하고 타입도 맞는데, useEffect 안에서 상태를 폭포처럼 줄줄이 갱신하거나 dangerouslySetInnerHTML을 별 고민 없이 넣어두는 식이죠. 에이전트 입장에서는 "요구사항을 만족시키는 코드"를 짠 것이지만, 우리 입장에서는 "리뷰 코멘트가 쏟아질 코드"입니다. 이 간극을 메우는 도구가 Million.co에서 만든 React Doctor입니다. 한 줄짜리 명령으로 React 프로젝트를 진단해서 0~100 사이의 건강

DNS 리바인딩(DNS Rebinding) 공격과 방어

DNS 리바인딩(DNS Rebinding) 공격과 방어

집에 있는 공유기 관리자 페이지(192.168.0.1)에 로그인해 본 적 있으시죠? 보통은 같은 와이파이에 연결된 기기에서만 접근할 수 있으니까 비밀번호를 admin/admin으로 그대로 두는 분들도 적지 않을 텐데요. 그런데 만약 인터넷 어딘가의 평범해 보이는 광고 배너 한 번 클릭한 것만으로 그 공유기에 누군가 마음대로 들어올 수 있다면 어떨까요? 😱 말도 안 되는 소리 같지만 실제로 이런 일을 가능하게 만드는 공격 기법이 있습니다. 바로 DNS 리바인딩(DNS Rebinding)인데요. 동일 출처 정책이 있으니까 외부 사이트

Web Crypto API로 브라우저에서 암호화 다루기

Web Crypto API로 브라우저에서 암호화 다루기

자바스크립트로 무언가를 만들다 보면 의외로 자주 마주치는 순간이 있습니다. 세션 토큰을 만들거나, 비밀번호를 해시하거나, 파일 무결성을 검증하거나, 쿠키에 서명을 넣어야 할 때인데요. 예전 같으면 별도의 라이브러리를 깔아야 했지만, 요즘은 브라우저와 서버 런타임 모두 이런 일을 표준 API로 해낼 수 있습니다. 바로 Web Crypto API입니다. 이 글에서 Web Crypto API의 사용법을 먼저 다루지만, 그 뒤에 깔린 대칭키와 비대칭키 암호화의 원리가 궁금하다면 별도 글에서 따로 풀어 두었습니다. 이름만 들으면 브라우저 전

Rolldown, Rust로 다시 쓴 Rollup 알아보기

Rolldown, Rust로 다시 쓴 Rollup 알아보기

Rollup은 이미 잘 만들어진 번들러인데, 왜 갑자기 이름이 비슷한 Rolldown이라는 도구가 등장했을까요? 🤔 게다가 Vite는 8 버전부터 오랫동안 써 온 Rollup을 두고 이 Rolldown을 기본 번들러로 채택했습니다. 멀쩡히 돌아가던 도구를 갈아엎은 데는 그만한 이유가 있겠죠. 최근 JavaScript 도구들이 하나둘 Rust로 다시 작성되는 흐름 위에 Rolldown도 놓여 있습니다. SWC가 Babel을, Oxc가 ESLint를 겨냥했다면, Rolldown은 Rollup의 자리를 노립니다. 이번 글에서는 Roll

TanStack AI: 프레임워크에 종속되지 않는 AI SDK

TanStack AI: 프레임워크에 종속되지 않는 AI SDK

AI 기능을 웹 앱에 붙이는 일이 요즘 정말 흔해졌습니다. 채팅 인터페이스를 만들거나, LLM에게 도구를 쥐여주거나, 스트리밍 응답을 화면에 뿌리거나. 그런데 막상 시작하면 고민이 생깁니다. OpenAI를 쓸지 Anthropic을 쓸지, React인지 Vue인지, Next.js인지 다른 프레임워크인지에 따라 코드가 전부 달라지거든요. TanStack Query와 TanStack Router로 유명한 TanStack 생태계에서 이 문제를 해결하려고 나온 게 바로 TanStack AI입니다. 스스로를 "AI 도구의 스위스"라고 소개할

Void: Vite 네이티브 배포 플랫폼

Void: Vite 네이티브 배포 플랫폼

Vite+가 로컬 개발 도구를 하나로 통합했다면, 이번에는 배포까지 먹으러 왔습니다. VoidZero가 내놓은 Void는 Vite 앱에 플러그인 하나를 추가하고 void deploy 한 방이면 데이터베이스, 인증, KV 스토리지, 큐까지 갖춘 풀스택 애플리케이션이 Cloudflare Workers 위에 올라가는 배포 플랫폼입니다. "자바스크립트에 드디어 Rails 순간이 온 건가, 아니면 그냥 Cloudflare 종속에 리본을 달아놓은 건가?" 궁금해서 Void가 뭘 하는 건지 직접 파헤쳐봤습니다. 2026년 9월에 나온 0.20

Storybook MCP로 AI에게 컴포넌트 맥락 알려주기

Storybook MCP로 AI에게 컴포넌트 맥락 알려주기

AI 코딩 에이전트에게 UI를 만들어달라고 하면 종종 난감한 결과물이 나옵니다. 프로젝트에 이미 잘 만들어둔 Button, Card, Modal 같은 컴포넌트가 있는데 에이전트는 그 존재를 모르니까 비슷한 걸 또 만들어버리는 거죠. 디자인 시스템을 열심히 구축해놨는데 AI가 인라인 스타일로 대충 때운 컴포넌트를 내놓으면 정말 답답합니다 😅 이 문제의 원인은 간단합니다. AI 에이전트에게 우리 프로젝트의 UI 컴포넌트에 대한 맥락이 없기 때문인데요. Storybook이 MCP(Model Context Protocol)를 통해 이 문

Discord