개발 팁
프로젝트와 상관없이 다시 쓸 수 있는 요령을 문제·원인·해법·코드 예시로 짧게 적습니다.
- 애플리케이션 keepalive는 클라이언트 타임아웃보다 짧게 보낸다 →클라이언트 라이브러리가 「아무것도 못 받으면 재접속」하는 시간을 확인하고, 그보다 자주 상태를 바꾸지 않는 메시지를 보낸다.
- Django async(웹소켓) 서버 성능 점검 목록 →DB 연결 수명, thread_sensitive, 전달 경로에서 저장 떼기, 채널 용량, 코어 수만큼 프로세스. 부하 때 지연이 튀면 이 다섯 곳부터 본다.
- CRDT(Yjs) 문서는 평평한 키로, 큰 숫자 열은 바이너리 하나로 둔다 →동시에 생길 수 있는 구조는 합쳐지는 단위로 나누고, 통째로 쓰고 잘 안 바뀌는 데이터는 압축해 한 값으로 둔다.
- 시험 더블과 운영 백엔드가 다르면, 실제 백엔드로 도는 시험을 하나는 둔다 →인메모리 레이어·SQLite·가짜 큐와 실제 구현의 차이에서만 나는 버그는 단위 시험이 못 잡는다. 핵심 경로 하나는 운영과 같은 백엔드로 돌린다.
- 운영 파이썬 프로세스 안을 재시작 없이 들여다보기 — sys.remote_exec →Python 3.14부터 살아 있는 프로세스에 스크립트를 주입해 세션 수, 객체 수, 할당자 상태를 볼 수 있다. 재시작하면 사라지는 문제를 그 자리에서 잡는다.
- touch-action: none을 주면 브라우저 제스처를 직접 만들어야 한다 →그리기 캔버스에 touch-action: none을 주면 손가락 스크롤까지 막힌다. 한 손가락은 그리기, 두 손가락은 스크롤로 직접 나눈다.
- 헬스체크는 싸게, 뜨는 동안만 촘촘히, 접근 로그에서는 뺀다 →검사 한 번의 비용을 간격으로 나눈 값이 상시 CPU다. 가벼운 도구로 검사하고, start_interval로 뜨는 동안만 촘촘히 보고, 헬스 요청은 접근 로그에서 거른다.
- 드래그·그리기 계산은 화면 좌표가 아니라 대상 좌표계에서 한다 →드래그 중에도 스크롤과 확대가 일어난다. 포인터를 대상 좌표로 바꾼 뒤 차이를 구하고, 스크롤 때 다시 계산한다.
- 파이썬 서버가 빈 메모리를 돌려주게 하는 glibc 설정 세 가지 →MALLOC_ARENA_MAX로 arena 수를 줄이고, mmap·trim 기준을 고정하고, 큰 데이터를 내린 뒤와 주기적으로 malloc_trim을 부른다.
- 무중단 배포에서 새 NOT NULL 컬럼에는 DB 기본값(db_default)을 준다 →마이그레이션 뒤 아직 교체되지 않은 옛 코드는 새 컬럼을 모른다. 파이썬 default가 아니라 DB DEFAULT가 있어야 옛 코드의 INSERT가 깨지지 않는다.
- 저장·통신 형식을 바꿀 때는 읽기를 먼저 배포하고, 쓰기는 다음 배포에서 →옛 클라이언트와 새 서버, 새 클라이언트와 옛 데이터가 함께 도는 시간이 반드시 있다. 두 형식을 다 읽게 먼저 배포하고, 새 형식 쓰기는 다음 배포에서 켠다.
- 새 서버로 옮기면 컨테이너 안 DNS부터 확인한다 →호스트에서는 이름이 풀리는데 docker 빌드·컨테이너에서만 EAI_AGAIN이 나면 브리지 네트워크의 DNS 문제다.
- OAuth 콜백은 다시 온다 — 성공한 로그인은 멱등하게 →뒤로 가기나 인앱 브라우저 복원으로 같은 state의 콜백이 다시 온다. 이미 로그인한 사용자라면 오류 대신 돌아갈 주소로 보낸다.
- RSS가 줄지 않을 때는 누수인지 할당자 보유인지 먼저 가른다 →타입별 객체 수가 안정적이면 누수가 아니라 할당자가 빈 메모리를 들고 있는 것이다. 객체 수 → mallinfo2 → malloc_trim 순서로 나눠 본다.
- 도커 소켓을 공유하는 CI에서는 bind mount를 쓰지 않는다 →CI 컨테이너가 호스트 도커 소켓으로 compose를 돌리면 마운트 경로는 호스트 기준이고, 없는 경로는 빈 디렉터리로 생긴다.
- 팝오버·드롭다운·툴팁은 body 포털로 렌더한다 →부모의 overflow·transform·z-index 쌓임 맥락에 갇히면 팝오버가 잘리거나 다른 요소에 가려진다. body에 렌더하고 위치는 직접 계산한다.
- channels_redis에서 직접 만드는 채널도 new_channel()로 만든다 →RedisChannelLayer는 프로세스당 받기 잠금이 하나라, 접두어가 다른 채널이 잠금을 쥐면 다른 채널의 메시지가 멈춘다.
- sticky·fixed 위치는 헤더의 실제 높이를 CSS 변수로 받아 쓴다 →top: 64px처럼 숫자를 박으면 모바일에서 헤더가 높아질 때 아래 툴바가 헤더 뒤로 숨는다. 헤더가 제 높이를 재서 CSS 변수로 알린다.
- 잘라야 하는 컨테이너는 overflow: hidden 대신 clip →overflow: hidden은 스크롤 컨테이너라 편집기·focus·scrollIntoView가 몰래 스크롤할 수 있다. 자르기만 하려면 overflow: clip을 쓴다.
- 캔버스 크기에는 픽셀 수 상한을 둔다(iOS Safari) →확대·고배율 화면에서 캔버스 픽셀 수가 브라우저 한도를 넘으면 캔버스가 빈 화면이 된다. 상한을 넘으면 배율을 그만큼 줄여 그린다.