실무에서 저장 프로시저는 권장되지 않는다. 이유를 따라가면 “비즈니스 로직을 어느 층에 둘 것인가”라는 질문이 된다. 답이 왜 로직 계층인지도 거기서 드러난다.
비즈니스 로직이 놓이는 계층
요즘 서비스는 3-tier 아키텍처로 만든다. 프레젠테이션 계층은 사용자에게 보이는 부분, 로직 계층은 회원 가입이나 상품 검색 같은 비즈니스 로직, 데이터 계층은 회원 정보와 상품 정보를 저장하고 제공하는 부분을 맡는다.
프로시저를 쓴다는 것은 데이터 계층에 비즈니스 로직을 두는 것이다. 아래의 모든 장단점이 여기서 나온다.
데이터 계층에 두면 얻는 것
애플리케이션에 투명하게 동작한다. 로직을 고칠 때 서버 코드는 프로시저를 호출만 하고 있으니 DB의 프로시저만 바꾸면 끝이다. 로직 계층에 두었다면 컴파일하고 빌드해서 배포 파일을 만들어야 하고, 트래픽을 받는 중이니 인스턴스도 한 대씩 내렸다 올려야 한다.
네트워크 트래픽도 준다. 애플리케이션 서버와 DB 서버는 다른 기계라서 로직이 애플리케이션에 있으면 쿼리마다 왕복이 생기는데, 프로시저 안에서 처리하면 호출 한 번으로 끝난다.
여러 서비스가 재사용할 수도 있다. 자바, 파이썬, 자바스크립트로 각각 만든 세 서비스가 같은 로직을 쓴다면 세 번 구현하는 대신 프로시저 하나를 공유하면 된다. 주민등록번호나 신용카드 정보처럼 민감한 데이터라면 테이블을 직접 열어주지 않고 프로시저만 열어주는 방법도 있다.
그 이득이 뒤집히는 자리
장점 네 가지가 대부분 그대로 뒤집힌다.
유지보수 비용이 커진다. 로직이 두 곳에 흩어져서 소스 코드와 프로시저를 각각 버전 관리해야 한다. 자바만 알면 되던 것이 프로시저 문법까지 알아야 하고, 기능 하나 추가하는 데 프로시저를 고치고 호출하는 코드를 고치고 배포까지 다시 해야 한다. 프로시저 쪽은 복잡하고 유연한 코드를 쓰기 어렵고 가독성도 디버깅도 떨어진다.
DB 서버는 늘리기 어렵다. 이것이 가장 무거운 단점이다. 로직이 DB에 있으면 DB 서버의 CPU와 메모리 사용량이 커지는데, 트래픽이 갑자기 몰릴 때 대응할 방법이 없다. 애플리케이션 서버는 데이터를 갖고 있지 않으니 복제해서 붙이면 그만이고 오토 스케일링도 걸어둘 수 있지만, DB 서버는 새로 붙여도 데이터가 없다. 기존 데이터를 전부 복제하는 시간을 유사시에 기다릴 수는 없다.
투명성이 항상 보장되지도 않는다. 프로시저 이름을 바꾸면 호출하는 쪽이 전부 깨진다. 새 이름으로 만들어두고 애플리케이션을 하나씩 바꿔가며 재가동한 뒤 마지막에 옛것을 지워야 한다. 소스 코드에 로직이 있을 때보다 오히려 손이 많이 간다.
투명한 것이 늘 좋지도 않다. 본문만 바꾸면 애플리케이션을 안 건드리고 배포되는데, 그 변경에 버그가 있으면 되돌리기 전까지 모든 서버의 트래픽이 영향을 받는다. 로직 계층이었다면 한 대씩 재가동하면서 모니터링하다가 이상을 발견해 롤백하므로, 같은 버그라도 서버 네 대 중 한 대분의 트래픽만 다친다. 번거로운 배포 절차가 곧 검증 단계다.
재사용은 양날의 검이다. 여러 서비스가 같은 프로시저를 쓰는데 한 서비스의 트래픽이 폭증하면 나머지 서비스까지 같이 느려진다. 그래서 DB 앞에 데이터를 관리하는 서비스를 하나 세우고 REST API로 인터페이스를 주는 구조를 택하게 되는데, 그러면 프로시저를 쓰는 곳이 그 데이터 서비스 하나뿐이라 재사용이라는 명분 자체가 사라진다.
정보 제한도 완벽하지 않다. 프로시저가 반환값으로 민감한 값을 넘겨주면 개발자는 그 값을 그대로 본다. 테이블 접근을 막으면 개발과 고객 응대 업무의 속도만 떨어진다. 보안은 담당자에게만 권한을 주고 민감한 정보를 암호화해 저장하며 보안서약서 같은 정책으로 받치는 편이 낫다.
로직 계층에서 응답 속도 메우기
남는 것은 응답 속도인데 이쪽도 로직 계층에서 메울 수 있다. 순서를 지킬 필요가 없는 쿼리라면 스레드 풀이나 논블로킹 IO로 동시에 보낸다. 순차 실행이 강제되는 구간에는 캐시를 둔다. 레디스에 결과를 60초짜리 수명으로 넣어두면 같은 요청은 한 번만 DB까지 간다. 응답 속도를 올리면서 DB 부하까지 줄이는 흔한 패턴이다.