내가 매번 다시 쓰는 패턴들
프로젝트가 바뀌어도 손이 먼저 가는 코드가 있다. 한 번 짜면 잘 안 바뀌고, 다음 프로젝트에서도 그대로 다시 쓰게 되는 작은 조각들이다. 모아 두면 나만의 표준 라이브러리가 되는데, 결국엔 다 머릿속에 들어 있어서 매번 새로 친다.
이 글은 그런 조각 네 개에 대한 메모다.
1. 타입이 박힌 환경변수
환경변수는 모두 string이다. 그런데 코드에서는 boolean이고, number이고, URL이고, enum이다. 이 간극을 매 프로젝트마다 똑같이 한 번 메우고 시작한다.
import { z } from 'zod';
const Env = z.object({
PORT: z.coerce.number().default(3000),
DEBUG: z.enum(['true', 'false']).transform(v => v === 'true').default('false'),
DATABASE_URL: z.string().url(),
ALLOWED_EMAIL: z.string().email(),
});
export const env = Env.parse(process.env);
이 한 블록 덕분에 process.env.PORT * 2 같은 코드가 깨지지 않는다. 그리고 더 중요하게, 누락된 환경변수가 런타임 한참 후가 아니라 부팅 직후에 비명을 지른다.
2. "그룹별 최신 한 건"
SQL을 쓸 때 가장 자주 마주치는 패턴 하나. 사용자별 최신 주문, 포스트별 최신 댓글, 디바이스별 마지막 로그인. 처음엔 매번 새로 짜다가, 결국 window function 하나로 굳어졌다.
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY created_at DESC
) AS rn
FROM orders
) ranked
WHERE rn = 1;
GROUP BY + JOIN 조합보다 읽기 쉽고, 인덱스만 잘 깔리면 빠르다. SQLite도 D1도 PostgreSQL도 같은 문법이다.
3. find를 잊는 데 일주일
맥에서 find로 파일 찾는 걸 그만뒀다. fd로 바꾸면 .gitignore/node_modules 자동 제외, 멀티스레드, 정규식 default. 다시 돌아갈 이유가 없다.
# 옛날
find . -name "*.tsx" -not -path "*/node_modules/*" | head -20
# 지금
fd '\.tsx$' | head -20
타이핑이 1/3로 줄고, 결과는 더 빠르다. 새 머신 셋업할 때 가장 먼저 까는 도구 셋 중 하나(나머지는 rg, bat).
4. JSON으로 끝내는 설정
설정 파일을 JS/TS로 짤지 JSON/YAML로 짤지 항상 고민했다. 결론: 변경 가능한 데이터는 JSON, 동작이 들어가면 TS. 둘을 섞지 않는다.
{
"site": {
"title": "Blog",
"author": "esc5221",
"baseUrl": "https://blog.example.com"
},
"build": {
"perPage": 10,
"feedLimit": 30
}
}
JSON은 다른 언어에서도 읽힌다. 백업 스크립트, CI 워크플로우, 외부 도구. 한 번의 추상화 비용이 평생 환영받는다.
마지막
이 패턴들은 천재적이지 않다. 어느 시니어 엔지니어한테 물어봐도 비슷한 답이 나올 거다. 하지만 "이걸 매번 다시 짠다"는 사실 자체가 의미 있는 신호다 — 자주 쓰는 것은 결국 본인 도구가 된다.