HTTP

25 posts
AI 크롤러를 막으면 돈이 될까?

AI 크롤러를 막으면 돈이 될까?

1년 전 Cloudflare가 꽤 과감한 일을 벌였습니다. 2025년 7월 1일을 "콘텐츠 독립기념일"이라 부르면서, 신규 도메인에서는 AI 크롤러를 기본으로 차단하도록 정책을 뒤집었거든요. 그동안 웹의 기본값은 "일단 열려 있음"이었는데, 그걸 "먼저 허락을 구하라"로 바꾼 겁니다. 여기에 크롤링 한 건마다 요금을 매기는 Pay Per Crawl까지 얹었습니다. 30년 가까이 예약석으로만 남아 있던 402 Payment Required 상태 코드를 꺼내 쓴 것도 이때였고요. 저처럼 개인 블로그를 운영하는 사람 입장에서 궁금한 건

AI 에이전트가 스스로 결제하는 웹: x402 프로토콜

AI 에이전트가 스스로 결제하는 웹: x402 프로토콜

AI 에이전트에게 일을 시키다가 이런 벽에 부딪혀 보신 적 있나요? 필요한 데이터를 가져오라고 시켰더니 "해당 API는 인증이 필요합니다"라며 멈춰 서는 상황 말이죠. 그 API를 쓰려면 사람인 제가 직접 회원가입을 하고, 신용카드를 등록하고, 요금제를 고르고, 발급받은 키를 환경 변수에 꽂아줘야 합니다. 에이전트가 자율적으로 일한다더니 결제 앞에서는 매번 저를 부르는 셈입니다. 😅 생각해보면 당연합니다. 웹의 결제는 처음부터 끝까지 사람을 전제로 설계됐거든요. 결제 양식을 채우고 약관에 동의하고 인증 문자를 받는 과정은 전부 사

Web Bot Auth: IP 대신 서명으로 봇의 신원을 증명하기

Web Bot Auth: IP 대신 서명으로 봇의 신원을 증명하기

서버 로그에 GPTBot이 찍혀 있다고 해봅시다. 이게 정말 OpenAI가 보낸 요청일까요? 🤔 User-Agent 헤더는 그냥 문자열입니다. curl -A "GPTBot" 한 줄이면 누구나 GPTBot 행세를 할 수 있죠. 그래서 지금까지는 IP 주소로 한 번 더 확인했습니다. 봇 운영자가 자기 IP 대역을 공개하면 사이트는 요청이 그 대역에서 왔는지 대조하는 방식이었는데요. 그런데 이 방식이 요즘 급격히 무너지고 있습니다. AI 에이전트가 사용자를 대신해 웹을 돌아다니기 시작하면서부터입니다. 이런 에이전트는 서버리스 환경이나

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

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

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

Rust HTTP 모킹: mockito 크레이트 사용법

Rust HTTP 모킹: mockito 크레이트 사용법

웹 API를 호출하는 코드를 작성하다 보면 항상 마주치는 고민이 있는데요. "이 코드는 어떻게 테스트하지?" 하는 문제입니다. 실제 API를 호출하는 테스트는 네트워크 상태에 따라 결과가 달라지고, 외부 서비스의 응답을 마음대로 흉내내기도 어렵죠. 이럴 때 유용한 것이 바로 HTTP 모킹 라이브러리인데요. Rust 생태계에서는 mockito 크레이트가 가장 널리 사용되고 있습니다. 이 글에서는 mockito를 사용해서 HTTP 요청을 보내는 코드를 어떻게 테스트하는지 단계별로 살펴보겠습니다. mockito란? mockito는 Rus

Let's Encrypt: 무료 인증서가 HTTPS의 문턱을 어떻게 낮췄나

Let's Encrypt: 무료 인증서가 HTTPS의 문턱을 어떻게 낮췄나

HTTPS를 처음 켜본 사람이라면 한 번쯤 같은 장면을 거칩니다. 인증서를 사려고 검색하다 보니 1년에 몇 만 원짜리부터 수십만 원짜리까지 등급이 나뉘어 있고, 어떤 걸 골라야 할지부터 막막한데요. 사이드 프로젝트 하나 띄워보자고 비싼 인증서를 사기는 부담스럽지만, HTTP로 두자니 브라우저가 "안전하지 않음"이라고 경고를 띄웁니다. TLS 인증서의 구조와 신뢰 체인이 궁금하다면 인증서 자체를 다룬 글에서 먼저 살펴볼 수 있습니다. 이 진입 장벽을 거의 무너뜨린 것이 Let's Encrypt입니다. ISRG(Internet Secu

SSL 인증서 수동 관리 입문: 발급부터 설치, 갱신까지

SSL 인증서 수동 관리 입문: 발급부터 설치, 갱신까지

요즘은 HTTPS를 켜는 일이 너무 쉬워졌습니다. Cloudflare 뒤에 사이트를 두거나 Caddy로 서버를 띄우면 인증서라는 단어를 한 번도 마주치지 않고 자물쇠가 켜지는데요. 그러다 회사 사내 서버나 공유 호스팅처럼 자동화가 없는 환경을 만나면 갑자기 낯선 단어들이 쏟아집니다. CSR을 만들라고 하고, 파일이 세 개 오고, 중간 인증서를 붙이라고 하고, 만료 30일 전부터만 갱신이 된다고 합니다. 이 낯섦은 능력의 문제가 아니라 세대 차이입니다. Let's Encrypt 이전 시대에는 인증서가 자동으로 흐르는 인프라가 아니라

Axum으로 REST API 서버 만들기: 라우팅, 추출자, 상태 관리

Axum으로 REST API 서버 만들기: 라우팅, 추출자, 상태 관리

Rust로 HTTP 서버를 짜다 보면 프레임워크 선택에서 한 번씩 막힙니다. actix-web은 성능이 검증됐지만 액터 모델이 낯설고, warp는 함수형 스타일이 깔끔한데 타입 에러가 미로처럼 느껴질 때가 있습니다. Axum은 Tokio 팀이 만든 웹 프레임워크입니다. "Tower 위의 얇은 라우팅 레이어"를 표방하는데, 이게 생각보다 실용적입니다. 타임아웃이나 인증 미들웨어를 Tower로 짜두면 Axum에 그대로 끼울 수 있고, 핸들러 함수 시그니처에 타입만 선언해두면 Axum이 요청에서 필요한 데이터를 알아서 꺼내줍니다. 이 글

Rust HTTP 클라이언트: reqwest 크레이트 사용법

Rust HTTP 클라이언트: reqwest 크레이트 사용법

웹 API를 호출하거나 외부 서비스와 통신해야 하는 상황은 어떤 언어로 개발하든 빈번하게 마주치게 됩니다. Rust에서는 이런 HTTP 통신을 위해 reqwest라는 크레이트가 사실상 표준처럼 사용되고 있는데요. Python의 requests 라이브러리처럼 직관적인 API를 제공하면서도 Rust답게 타입 안전성과 비동기 처리를 지원하는 것이 특징입니다. 이 글에서는 reqwest 크레이트의 기본적인 사용법부터 실무에서 자주 쓰이는 패턴까지 예제와 함께 살펴보겠습니다. reqwest란? reqwest는 Rust 생태계에서 가장 널리

Rust http 크레이트: HTTP 생태계의 공용 어휘

Rust http 크레이트: HTTP 생태계의 공용 어휘

Rust로 HTTP를 다뤄본 분이라면 한 가지 묘한 사실을 눈치채셨을 거예요. reqwest로 요청을 보내든, hyper로 저수준 서버를 짜든, axum으로 핸들러를 만들든 같은 타입 이름이 자꾸 등장합니다. StatusCode, HeaderMap, Method, Uri 같은 것들이요. 이건 우연이 아닙니다. 이 타입들은 모두 http라는 별도의 크레이트에 정의되어 있고, Rust HTTP 생태계 전체가 이 어휘를 공유하고 있는 거죠. 덕분에 reqwest로 받은 응답의 헤더를 axum 응답에 그대로 옮길 수 있고, tower 미들

AI 크롤러에게 통행료 받기: Cloudflare Pay Per Crawl

AI 크롤러에게 통행료 받기: Cloudflare Pay Per Crawl

혹시 여러분이 운영하는 웹사이트의 서버 로그에서 AI 크롤러가 하루에 몇 번이나 다녀가는지 확인해보신 적 있나요? 저는 개인 블로그의 로그를 살펴보다가 GPTBot, ClaudeBot, Bytespider 같은 봇들이 사람 방문자 못지않게 부지런히 드나드는 걸 보고 묘한 기분이 들었습니다. 제 글이 AI 모델의 학습 자료나 답변 재료로 쓰일 수 있는데, 그 사용 조건은 제가 정할 수 없으니까요. 웹에는 오랫동안 콘텐츠를 무료로 공개하는 대신 트래픽과 인용으로 보상받는 암묵적인 상생 구조가 있었습니다. 하지만 AI 크롤러는 이 구조를

Discord REST API 직접 호출하기: 인증과 레이트 리밋

Discord REST API 직접 호출하기: 인증과 레이트 리밋

디스코드 봇을 만들어 서버에 초대해 뒀다면, 이제 그 봇으로 실제로 무언가를 시켜볼 차례인데요. 가장 먼저 해볼 일은 채널에 메시지 하나 보내는 겁니다. 보통은 discord.js 같은 라이브러리가 이걸 한 줄로 처리해 주지만, 이번 글에서는 라이브러리를 걷어내고 Discord REST API를 맨손으로 직접 호출해 보겠습니다. 굳이 왜 이걸 알아야 할까요? 우선 라이브러리가 무엇을 대신해 주는지 알면 문제가 생겼을 때 디버깅이 쉬워집니다. 또 Cloudflare Workers 같은 서버리스 환경에서는 무거운 라이브러리 없이 fet

htmx로 JavaScript 없이 동적 웹페이지 만들기

htmx로 JavaScript 없이 동적 웹페이지 만들기

웹 개발을 하다 보면 한 가지 의문이 드는 순간이 있습니다. 버튼 하나 클릭했을 때 서버에서 데이터 받아와서 화면 일부만 바꾸고 싶은데, 꼭 React나 Vue 같은 프레임워크를 써야 할까? 간단한 검색 폼이나 좋아요 버튼 하나 만드는데 JavaScript를 수십 줄씩 작성하는 게 과연 맞는 걸까? 이런 고민은 사실 꽤 오래전부터 있었어요. SPA(Single Page Application)가 대세가 되면서 JavaScript 코드는 점점 늘어났고 빌드 도구도 복잡해졌고 번들 크기도 커졌죠. 그런데 정작 만들고 싶었던 건 "클릭하면

웹 개발자를 위한 HTTP 상태 코드 안내서

웹 개발자를 위한 HTTP 상태 코드 안내서

웹 개발자라면 200, 404, 500 같은 HTTP 상태 코드를 한 번쯤은 접해보셨을 텐데요. 자주 보는 코드의 뜻은 대강 알고 있어도, 막상 API를 설계하려면 어떤 코드를 골라야 할지 헷갈릴 때가 있습니다. 400과 422, 502와 504처럼 비슷해 보이는 코드도 있고요. 혹시 HTTP 메시지 구조나 버전 변천이 궁금하다면 HTTP 한눈에 보기에서 큰 그림을 먼저 살펴보셔도 좋습니다. 이번 글에서는 웹 개발에서 자주 만나는 HTTP 상태 코드를 범주별로 살펴보고, 서로 비슷한 코드를 어떤 기준으로 구분해야 하는지 알아보겠습니

쿠키 2부: 세션은 쿠키가 필요해~

쿠키 2부: 세션은 쿠키가 필요해~

“사용자 인증을 할 때 쿠키를 사용하면 위험하고요 서버에 데이터를 저장하는 세션을 사용하는 것이 안전해요.” 사용자 인증에 대해서 논할 때 자주 듣게 되는 얘기인데요. 과연 이 말이 맞는 말일까요? 저한테는 굉장히 모순된 얘기로 들리는 것 같습니다. 많은 분들이 쿠키(cookie)와 세션(session)을 서로 대립하거나 세션이 쿠키를 대체하는 기술로 오해하는 것 같은데요. 사실 쿠키와 세션은 상호 보완을 하는 기술이라고 보는 것이 더 맞을 것입니다. 지난 포스팅에서는 서버가 브라우저에 쿠키를 어떻게 저장하고 쿠키라는 기술의 한계에

쿠키 1부: HTTP로 설명하는 쿠키(cookie)

쿠키 1부: HTTP로 설명하는 쿠키(cookie)

"쿠키는 클라이언트에 저장되고... 음,,, 보안에 좋지 않습니다." 😅 개발자 면접을 볼 때 쿠키에 대해서 물어보면 가장 흔하게 들을 수 있는 대답인데요. 완전히 틀린 말은 아니지만 뭔가 알맹이가 빠진 느낌이 듭니다. 쿠키가 왜 등장했는지를 이해하려면 HTTP가 무상태(stateless) 프로토콜이라는 점을 먼저 떠올려보면 좋습니다. "그럼 보안을 위해서 쿠키는 안 쓰는 게 좋겠네요?" 라고 반문을 하면 오랫동안 웹 개발을 한 분들도 머뭇거리시는 경우가 많은데요. 아무래도 대부분의 서버 프레임워크에서 쿠키를 직접 다루지 않아도

파이썬에서 requests 라이브러리로 원격 API 호출하기

파이썬에서 requests 라이브러리로 원격 API 호출하기

requests는 파이썬으로 HTTP 통신이 필요한 프로그램을 작성할 때 가장 많이 사용되는 라이브러리입니다. 특히 원격에 있는 API를 호출할 때 유용하게 사용할 수 있는데요. 이번 포스팅에서는 requests 라이브러리를 사용하는 방법에 대해서 알아보겠습니다. 패키지 설치 파이썬의 패키지 매니저인 pip를 이용해서 requests 패키지을 설치합니다. 설치가 잘 되었는지 파이썬 인터프리터를 실행하여 확인해봅니다. requests 라이브러리로 구글에 접속을 해보니 상태 코드 200이 응답이 되는 것을 볼 수 있습니다. 🎉 API

자바스크립트의 fetch() 함수로 원격 API 호출하기

자바스크립트의 fetch() 함수로 원격 API 호출하기

JavaScript, API, Markup를 근간으로 하는 JAM stack이 모던 웹 개발의 새로운 트랜드가 되고 있습니다. 이에 따라, 예전처럼 서버 단에서 대신 API를 호출해주기 보다는 클라이언트 단에서 직접 API를 호출하는 경우가 많아지고 있습니다. (이렇게 브라우저에서 직접 비동기로 HTTP 통신을 하는 것을 한 때 소위 Ajax라고도 일컬었죠...) 이번 포스팅에서는 원격 API를 간편하게 호출할 수 있도록 브라우저에서 제공하는 fetch() 함수에 대해서 살펴보겠습니다. 라이브러리? 원격 API 호출하면 제일 먼저

Caddy: 인증서를 신경 쓰지 않아도 되는 웹 서버

Caddy: 인증서를 신경 쓰지 않아도 되는 웹 서버

사이드 프로젝트를 서버에 처음 올려본 사람이라면 비슷한 밤을 보냈을 겁니다. 앱은 이미 3000번 포트에서 잘 돌아갑니다. 정작 반나절이 날아가는 쪽은 그걸 바깥에서 접속하게 만드는 일이죠. Nginx 설정 파일에서 server 블록과 location 블록의 중괄호를 세어 보고, Let's Encrypt 인증서를 받으려고 certbot을 깔고, 갱신이 제대로 도는지 확인하려고 cron 로그를 뒤지는 식입니다. 서비스 로직은 한 줄도 건드리지 않았는데 말이죠. 😅 Caddy는 바로 이 지점을 겨냥한 웹 서버입니다. Go로 작성된 오

Cache-Control 헤더 정리: max-age부터 stale-while-revalidate까지

Cache-Control 헤더 정리: max-age부터 stale-while-revalidate까지

웹 페이지를 두 번째 열 때 첫 번째보다 훨씬 빨리 그려지는 경험을 한 번쯤 해보셨을 겁니다. 어딘가에서 그 자원을 미리 들고 있다가 다시 꺼내준 결과인데, 이 "미리 들고 있다"가 곧 캐시이고요. 브라우저, 회사 프록시, CDN, 원본 서버 앞 모두 캐시가 자리할 수 있어 한 자원이 여러 곳에 사본을 가질 수 있는 셈입니다. 문제는 이렇게 흩어진 캐시들이 서로 다른 규칙으로 동작하면 곤란하다는 점입니다. 한 캐시는 1시간을 보관하고 다른 캐시는 영원히 보관하는 식이면 운영자가 원하는 정책을 만들 수가 없죠. 그래서 HTTP는 모든

Discord