커넥션 풀 크기를 몇으로 둘까 — pool_size·워커 수 계산법

커넥션 풀 크기를 정할 때 “일단 넉넉하게 잡아두면 안전하다”고 생각하는 경우가 많습니다. 하지만 코어 4개짜리 서버라면 HikariCP가 제시하는 공식으로 계산했을 때 적정 pool_size는 9 정도에 불과합니다. 100, 200씩 크게 잡아둔 풀이 오히려 DB 서버의 컨텍스트 스위칭 부하를 키우고 응답 속도를 떨어뜨리는 경우가 흔합니다. 이 글에서는 pool_size·워커 수·max_connections를 어떤 순서로 맞춰야 하는지, 실제 공식과 숫자로 짚어보겠습니다. … Read more

운영 중 컬럼 추가가 테이블을 잠그는 이유와 피하는 마이그레이션 순서

많은 분들이 ALTER TABLE ADD COLUMN을 가벼운 메타데이터 작업이라고 생각하시는데, 실제로는 운영 중 컬럼 추가가 테이블 전체를 잠그는 사고로 이어지는 경우가 많습니다. DEFAULT 값의 종류, NOT NULL 제약조건 유무, DB 엔진과 버전에 따라 잠금 범위와 지속 시간이 완전히 달라지기 때문입니다. 서비스 중단을 피하려면 nullable 컬럼을 먼저 추가하고, 데이터를 배치로 채운 다음, 제약조건은 검증 단계를 분리해서 … Read more

크롤러가 조용히 0건을 내놓을 때, 진짜 실패인지 판단하는 기준

크롤러를 운영하다 보면 “에러 로그가 없으니 정상 작동했다”고 믿는 경우가 많습니다. 하지만 크롤러가 조용히 0건을 내놓는 상황은 에러 한 줄 남기지 않고도 자주 일어납니다. 0이라는 숫자 자체만 보고 성공과 실패를 가르면 안 되고, 상태 코드·응답 크기·과거 수집량 같은 주변 신호를 같이 봐야 실패인지 정상인지 구분할 수 있습니다. 0건이 나오는 세 가지 서로 다른 상황 같은 … Read more

매번 전부 다시 긁지 않으려면: ETag와 304로 크롤러 재방문 주기 정하기

새벽마다 돌려둔 크롤러가 어제와 똑같은 페이지를 처음부터 끝까지 다시 내려받고 있는 걸 로그에서 발견하면 마음이 무거워집니다. 매번 전부 다시 긁지 않으려면 서버가 보내주는 ETag와 Last-Modified 값을 저장해두고, 다음 요청 때 If-None-Match·If-Modified-Since 헤더에 같이 담아 보내는 조건부 요청 방식을 쓰면 됩니다. 서버가 내용이 바뀌지 않았다고 판단하면 본문 없이 304 Not Modified만 돌려주기 때문에, 대상 페이지 수가 … Read more

같은 URL을 두 번 긁고 있다면 점검할 정규화와 방문 기록 설계

크롤러 속도가 느려지면 네트워크 지연이나 서버 응답 시간부터 의심하는 경우가 많은데, 실제로 요청 로그를 펼쳐보면 같은 URL을 두 번, 많게는 열 번 넘게 긁고 있다면 원인은 네트워크가 아니라 URL 정규화 누락인 경우가 훨씬 흔합니다. 쿼리스트링 순서나 대소문자, 트레일링 슬래시만 달라도 크롤러 입장에서는 완전히 다른 주소로 보이기 때문입니다. 이 글에서는 어떤 표기 차이를 정규화 대상으로 삼아야 … Read more

브라우저로 로그인하고 requests로 긁기: 쿠키·토큰 전달법과 한계

로그인이 필요한 사이트를 requests로 긁어오려다가 로그인 폼부터 막혀서 검색하셨다면, 답은 이렇습니다. 브라우저에서 수동으로 로그인한 다음 그 세션의 쿠키나 토큰을 꺼내 requests.Session()에 그대로 넘겨주면 로그인 페이지를 자동화할 필요 없이 로그인된 상태로 요청을 보낼 수 있습니다. 다만 httpOnly 쿠키, TLS 핑거프린팅, 자동 갱신되는 토큰 때문에 이 방법이 안 통하는 사이트도 적지 않아서, 어디까지 되고 어디서부터 막히는지를 같이 … Read more

로컬에선 되는데 서버에선 안 되는 헤드리스 크롤러, 원인 좁히기

결론부터 말씀드리면, 로컬에선 되는데 서버에선 안 되는 크롤러 문제는 거의 항상 “코드가 틀렸다”가 아니라 “환경이 다르다”에서 생깁니다. 브라우저 바이너리에 필요한 리눅스 라이브러리가 없거나, 서버의 데이터센터 IP가 차단당하거나, 화면이 없는 환경에서 폰트·언어 설정이 달라서 셀렉터가 어긋나는 세 가지 경우가 대부분을 차지합니다. 이 글에서는 Playwright와 Puppeteer 기준으로, 로컬과 서버의 차이를 하나씩 좁혀가는 순서를 정리합니다. 로컬 환경과 서버 … Read more

networkidle 대기가 타임아웃으로 끝날 때, 진짜 봐야 할 지점

로그인 여부를 가리려고 page.wait_for_load_state(“networkidle”, timeout=30000)을 걸어 뒀는데, 로그를 열어 보면 거의 매번 30000ms를 꽉 채우고 타임아웃이 떨어졌습니다. 네트워크 탭을 같이 열어 보니 요청이 멈추지 않아서가 아니라, 광고 스크립트 하나가 몇 초 간격으로 계속 비콘을 쏘고 있어서 “500ms 동안 조용한 순간”이 끝까지 오지 않는 상태였습니다. networkidle 대기가 타임아웃으로 끝날 때 정말 기다려야 하는 건 네트워크가 아니라, … Read more

무한 스크롤 페이지를 끝까지 긁기, 스크롤 반복이 항목을 빠뜨리는 이유

무한 스크롤 페이지를 끝까지 긁기 위해 스크롤 반복문을 돌렸는데 전체 항목 수보다 적게 나온다면, 범인은 대부분 document.body.scrollHeight 비교 방식입니다. 서버 응답이 스크립트의 대기 시간보다 늦게 끝나면 스크롤 루프는 “더 이상 늘어날 콘텐츠가 없다”고 착각하고 일찍 종료해 버립니다. 예를 들어 항목이 1,200개인 목록 페이지에서 이 방식을 쓰면 700~800개 선에서 멈추는 현상이 흔하게 나타나는데, 원인은 네트워크가 아니라 … Read more

긁을 때 브라우저를 띄워야 할까? 숨은 JSON API 찾는 순서

“크롤러로 사이트를 긁을 때 브라우저를 띄워야 할까요?” 답은 대부분 아니오입니다. 개발자 도구의 네트워크 탭을 먼저 열어서 페이지가 실제로 호출하는 JSON API를 찾아내면, 셀레니움이나 플레이라이트 같은 브라우저 자동화 없이도 데이터를 훨씬 빠르고 가볍게 가져올 수 있습니다. 다만 모든 사이트에 이 방법이 통하지는 않아서, 확인 순서와 브라우저가 꼭 필요한 예외 상황까지 함께 정리했습니다. 브라우저 렌더링과 API 호출, … Read more