FastAPI CORS 설정이 프로덕션에서만 막힐 때 점검 순서

로컬 개발 환경에서는 문제없이 잘 열리던 API가 배포만 하면 브라우저 콘솔에 CORS 에러를 뿌린다면, ‘FastAPI CORS 설정이 프로덕션에서만 막힐’ 때 뭐부터 봐야 하는지 찾아서 이 글에 오셨을 겁니다. 먼저 답을 드리면, 원인은 대부분 CORSMiddleware 코드 로직이 아니라 배포 환경 쪽에 있습니다. allow_origins에 등록한 도메인과 브라우저가 실제로 보내는 Origin이 문자 하나까지 일치하지 않거나, Nginx 같은 리버스 … Read more

LLM 임베딩을 캐시할 때, 키를 뭘로 잡아야 할까요

LLM 임베딩을 캐시할 때 키를 뭘로 잡아야 캐시 적중률이 떨어지지 않는지는, 실서비스에서 API 요금과 응답 속도를 동시에 좌우하는 문제입니다. 이 글에서는 텍스트 해시 하나만 믿고 키를 설계했을 때 실제로 생기는 문제와, 파이썬 코드로 안전하게 캐시 키를 만드는 방법을 다룹니다. 미리 정리하면, 정규화한 텍스트의 SHA-256 해시만으로는 부족하고 여기에 모델명·임베딩 차원 같은 파라미터를 함께 묶어야 캐시가 새지 … Read more

파이썬 스크립트가 서버 재부팅 후 안 살아날 때 자동 시작 등록법

서버를 재부팅했는데 돌려두었던 파이썬 스크립트가 다시 살아나지 않아서 검색하셨다면, 원인은 대부분 하나입니다. 그 파이썬 스크립트가 서버의 부팅 과정에 아예 등록되어 있지 않기 때문입니다. nohup python3 app.py &나 터미널에서 그냥 실행한 스크립트는 세션이 끝나거나 서버가 재부팅되면 함께 사라지고, 다시 켜주는 절차가 없으면 영영 안 살아납니다. 이 글에서는 systemd와 cron 두 가지 방식으로 실제 등록하는 절차, 그리고 … Read more

SQLite WAL 모드인데도 database is locked 뜨는 이유, 원인부터 좁혀보기

결론부터 말씀드리면, SQLite WAL 모드인데도 database is locked 에러가 나는 경우는 원인이 하나가 아닙니다. 에러 메시지 텍스트를 자세히 보면 두 갈래로 갈리는데, 단순히 “database is locked”만 뜨는지 아니면 “database table is locked”처럼 테이블 이름까지 붙어서 뜨는지가 첫 번째 단서입니다. 전자는 다른 커넥션과의 충돌(SQLITE_BUSY)이고, 후자는 같은 커넥션 안에서 커서를 안 닫아 생기는 자기 잠금(SQLITE_LOCKED)이라 해결 방법이 … Read more

LLM 답변에 최신 정보가 없을 때, 웹 검색 도구는 언제 필요할까요

어제 발표된 금리 인상 소식을 챗봇에게 물었더니, 몇 달 전 자료를 근거로 태연하게 틀린 답을 내놓은 경험이 있으실 거예요. LLM 답변에 최신 정보가 없을 때는 모델 자체를 바꾸기보다, 웹 검색 도구를 연결하는 쪽이 훨씬 빠르고 확실한 해결책입니다. 다만 이 도구를 붙이는 게 항상 정답은 아니라서, 언제 붙이고 언제 다른 방법을 써야 하는지 기준이 필요합니다. 왜 … Read more

파이썬 asyncio.gather에서 하나 실패하면 전체가 죽는 이유와 해결법

이 글에서는 파이썬 asyncio.gather에서 코루틴 하나만 실패해도 나머지 작업까지 통째로 멈춰버리는 상황을 코드로 재현하고, return_exceptions 옵션으로 이 문제를 피하는 방법을 정리합니다. 정답을 먼저 말씀드리면, asyncio.gather(*aws)를 기본 옵션 그대로 쓰면 그중 하나가 예외를 던지는 순간 gather 자체가 그 예외를 그대로 위로 전달해서 await 지점을 감싼 코드가 멈춰 버립니다. return_exceptions=True를 넣으면 실패한 작업의 예외 객체를 결과 리스트 … Read more

Docker Compose에서 서비스 재시작 순서가 꼬이는 진짜 원인

Docker Compose에서 서비스 재시작 순서가 꼬이는 원인은 대부분 depends_on을 오해하는 데서 시작됩니다. depends_on은 컨테이너를 “먼저 실행”만 시켜줄 뿐, 그 안의 애플리케이션이 요청을 받을 준비가 됐는지는 전혀 확인하지 않습니다. 이 글은 docker compose v2(CLI 플러그인) 기준으로, 어디까지가 자동으로 보장되고 어디서부터 직접 챙겨야 하는지를 실제 설정 예시로 정리합니다. depends_on이 보장하는 건 순서일 뿐, 준비 상태가 아닙니다 depends_on에 … Read more

LLM 요청에 캐시가 안 걸릴 때, 프롬프트 순서 점검법

이 글에서는 LLM 요청에 캐시가 걸릴 조건과, 실제로는 왜 캐시가 자꾸 깨지는지를 프롬프트 구조 관점에서 확인합니다. 결론부터 말씀드리면, 캐시는 요청 전체가 아니라 앞쪽 프리픽스(prefix)를 통째로 비교하기 때문에, 정적인 내용보다 앞에 단 한 글자라도 바뀌는 값(날짜, 세션 ID, 사용자 질문)을 배치하면 그 뒤 전체가 캐시 미스로 처리됩니다. 순서만 바꿔도 캐시 적중률이 크게 달라지는 이유가 여기에 있습니다. … Read more

파이썬 venv 대신 uv venv 써보니 달라진 점 정리

“파이썬 venv 대신 uv를 써도 되는지” 궁금해서 검색하셨다면, 답부터 말씀드릴게요. 가상환경을 만들고 패키지를 설치하는 루틴 작업이라면 uv venv로 바꿔도 거의 대부분 문제없이 대체됩니다. 다만 팀 전체가 아직 pip·venv 조합을 쓰고 있거나, 사내 네트워크가 외부 파이썬 배포판 다운로드를 막아둔 환경이라면 이야기가 조금 달라집니다. 이 글은 macOS와 Ubuntu 환경, Python 3.11 기준으로 uv 최신 버전을 직접 설치해서 … Read more

GitHub Actions로 배포 자동화할 때, SSH 키는 이렇게 지켜야 합니다

깃허브 공개 저장소에 SSH 개인키를 워크플로 파일에 그대로 적어 넣으면, git log로 과거 커밋만 거슬러 올라가도 키 전체가 그대로 남아 있습니다. 한 번 커밋된 값은 이후 삭제해도 히스토리에 흔적이 남기 때문에, 키를 되돌리는 게 아니라 폐기하고 새로 발급하는 것이 유일한 해결책입니다. GitHub Actions로 배포 자동화할 때 가장 먼저 손대야 할 부분이 바로 이 시크릿 관리이고, … Read more