DB 작업 규칙 — 저장 프로시저 금지
새로 만드는 기능은 저장 프로시저 대신 파라미터 SQL·ORM·트랜잭션을 쓴다. 기존 프로시저는 그 표를 고칠 때 점진적으로 옮긴다.
규칙
새로 만드는 모든 기능은 저장 프로시저(Stored Procedure) 없이 동작합니다.
- 표에 직접
INSERT/UPDATE/DELETE합니다 — 파라미터(바인딩) SQL 로. - ORM(Prisma, knex 등)을 쓰는 프로젝트는 ORM 을 그대로 씁니다.
- 이미 프로시저로 돌아가는 프로젝트도, 그 표를 고칠 일이 생기면 함께 옮깁니다.
왜
인자 자리가 한 칸 밀려도 타입 검사·린트·빌드가 전부 통과합니다. 운영에서야 드러납니다.
// 프로시저 — 인자 19개. 가운데 컬럼을 끼워 넣으면 뒤가 전부 밀린다
await sp.call('photos_SP', [0, projectSeq, /* … */, filePath, thumbPath, fileSize, grade /* … */]);
// ↑ 하나 끼면 fileSize 가 grade 칸으로
// 직접 쓰기 — 이름으로 넣으므로 밀릴 수가 없다
await prisma.photos.create({ data: { projectSeq, filePath, thumbPath, grade } });
| 비교 | 저장 프로시저 | 직접 쓰기 |
|---|---|---|
| 컬럼 하나 추가 | 프로시저 재작성 + 모든 호출부 수정 | 한 줄 |
| 인자 대응 | 위치(순서)로 — 사람이 세어야 함 | 이름으로 |
| 오류 발견 시점 | 운영 | 개발(타입·테스트) |
인자가 15개를 넘으면 사람이 세어 가며 맞추게 되고, 그러면 반드시 틀립니다. 프로시저를 감싸는 래퍼를 따로 만들게 됐다면, 그것이 프로시저가 맞지 않는다는 신호입니다.
여러 표를 한 번에 고쳐야 하면 — 트랜잭션
원자성(전부 성공 또는 전부 취소)이 필요하면 프로시저가 아니라 트랜잭션으로 묶습니다.
await prisma.$transaction([
prisma.orders.update({ where: { id }, data: { status: 'DONE' } }),
prisma.stock.update({ where: { itemId }, data: { qty: { decrement: n } } }),
]);
// knex
await knex.transaction(async (trx) => {
await trx('orders').where({ id }).update({ status: 'DONE' });
await trx('stock').where({ itemId }).decrement('qty', n);
});
문자열을 이어 붙여 SQL 을 만들지 않는다
// X — SQL 주입(injection) 위험
db.query(`SELECT * FROM cows WHERE name = '${name}'`);
// O — 파라미터 바인딩
db.query('SELECT * FROM cows WHERE name = ?', [name]);
기존 프로시저를 옮기는 순서
한꺼번에 걷어내지 않습니다.
- 새로 만드는 것은 처음부터 직접 쓰기로
- 인자가 많은 것부터 옮긴다
- 나머지는 그 표를 손댈 때 같이 옮긴다
옮길 때는 읽기 경로를 먼저 바꾸고(안전합니다), 쓰기는 한 표씩 바꾸며 전후 결과를 비교합니다.
문자셋
DB·테이블·접속 문자셋은 utf8mb4 로 맞춥니다. 조회 도구도 같은 문자셋을 지정해야 한글이 깨져 보이지 않습니다('문자 인코딩 — UTF-8 필수' 문서 참조).