12. 실무에서 프로시저
유튜버 쉬운코드 님의 데이터베이스 강의를 정리한 내용
# 실무에서 프로시저
실무에서 프로시저를 사용한다는 것은 권장되지 않는다. 왜 그럴까?
# 3-tier architecture
최근 실무에서는 client-server architecture의 한 종류인 3-tier architecture 모델로 서비스를 개발함
Logic tier
- 비즈니스 로직이란?
- ex) 당근마켓의 비즈니스 로직
- 회원 가입 / 탈퇴
- 상품 리스트업 알고리즘
- 상품 정보 업로드 기능
- 상품 검색 기능
- 메시지 기능
- etc …
Data tier
- 데이터?
- ex) 당근마켓의 데이터
- 회원 정보
- 상품 정보
- 판매 / 구매 내역
- 지역 정보
- etc …
- 즉, stored procedure를 사용한다는 것은 data tier에 비즈니스 로직이 생기게 된다는 의미이다.
- data tier에 비즈니스 로직이 생기게 된다는 것은 어떤 장/단점이 있을까 ??
# 프로시저 장점
stored procedure 장점
- Application에 투명(transparent)하게 동작할 수 있다.
- Network traffic을 줄여서 응답 속도를 향상시킬 수 있다.
- 여러 서비스에서 재사용 가능하다.
- 민감항 정보에 대한 접근을 제한할 수 있다.
# transparent
ex) stored procedure를 사용하지 않고 Logic tier에서만 비즈니스 로직을 관리한다고 생각해보자.
- 비즈니스 로직을 수정할 일이 생겼고, 현재 서버는 트래픽을 받고 있는 상황이다.
- 이제 배포해야하니까 컴파일시키고 배포파일 만든 이후에 기존의 인스턴스는 내리고 새로운 인스턴스를 띄워야 한다.
- 하지만, 동시에 모두 서버 애플리케이션을 껐다 키면 안된다. 트래픽을 받고 있는 상황이라 바꿔주는 동안 트래픽을 처리할 수 없다.
- 따라서, 나눠서 처리해야한다.
- 하나 바꾸고 재가동, 하나 바꾸고 재가동… 비즈니스 로직을 수정할 일이 있을 때마다, 컴파일 새로하고, 빌드해서 배포 파일 만들고, 한대 한대씩 재가동 하는 것은 귀찮아 보인다.
ex) 하지만, 이 비즈니스 로직이 stored procedure를 이용하여 관리된다면 어떻게 될까?
- Logic tier에서는 프로시저를 호출만 하고 있으니까, 서버 애플리케이션 쪽은 바꿀 필요 없음
- Data tier에 프로시저만 쏙 바꾸면 되는거다.
transparent 하다는 의미는, 뭔가를 바꿔도 바뀌기 전에 사용하고 있던 부분들은 바꾸지 않고도 내용을 바꿀 수 있다는 의미이다. 따라서, stored procedure는 application에 대해서 transparent 할 수 있다는 장점이 있는 것이다.
# 응답속도 감소
ex) stored procedure를 사용하지 않고 Logic tier에서만 비즈니스 로직을 관리한다고 생각해보자.
- 스프링 서버와 DB 서버는 다른 서버이기 때문에 쿼리문마다 호출해야 한다.
- 요청을 보내는 순간 네트워크 트래픽이 발생
ex) 하지만, 이 비즈니스 로직이 stored procedure를 이용하여 관리된다면 어떻게 될까?
- 어차피 비즈니스 로직이 DB 서버에서 처리되기 때문에 프로시저를 호출만 하면 된다.
- 네트워크 트래픽이 한 번만 왔다갔다 하면 된다.
- 따라서, Network traffic을 줄여서 응답 속도를 향상시킬 수 있다는 장점이 있다.
# 재사용 가능
ex) DB에 저장되어있는 data를 이용하는 서비스가 3개 있다고 하자.
- 각 서비스는 Java, Python, Javascript 각각으로 비즈니스 로직을 구현해야함
- stored procedure를 쓰면 여러 서비스에서 재사용 가능하다는 장점이 있다.
# 정보 제한
ex) 회사 내의 개발자나 임직원이 DB에 접근하는 것을 막는 경우
- 예를 들어, 사용자의 주민등록번호, 신용카드 정보 등등 민감한 정보에 접근을 막는 경우
- 하지만, 개발자가 개발은 해야하는 상황
- 개발자가 프로시저에 접근하는 것은 허용함으로써, 직접적인 접근은 막고 비즈니스 로직은 구현할 수 있도록 해주는 것이 stored procedure의 장점!!
# 프로시저 단점
stored procedure 단점
- 유지 관리 보수 비용이 커진다.
- DB 서버를 추가하는 것은 간단한 작업이 아님
- DB 서버의 CPU 사용량이나 메모리 사용량이 늘어나게 되고, 트래픽이 폭발적으로 증가한 유사시에 긴급하게 대응이 힘듦
- stored procedure가 언제나 transparent 인 것은 아님
- stored procedure가 transparent 하다고 무조건 좋은 것도 아님
- 재사용이 가능하다는 것은 양날의 검이 될 수도 있다.
- stored procedure가 민감한 정보에 대하여 접근을 완벽히 제한할 수 없음
- 또, DB 혹은 테이블 접근을 막으면 개발 및 CS 업무의 신속함이 떨어짐
- CS 업무는 Customer Service를 뜻함
# 유지보수 힘듦
ex) Logic tier에도 비즈니스 로직, Data tier에도 비즈니스 로직이 있는 경우
- Logic tier 봤다가~ Data tier 봤다가~ 왔다갔다 힘듦
- 소스 코드의 버전 관리도 해야하고, 프로시저의 버전 관리도 해야함
- 원래는 소스코드만 관리하면 되니까, 여기에서는 Java만 알면 되는데, 프로시저의 문법도 알아야함
- 만약, 신규 기능 개발시 이에 대한 비즈니스 로직을 프로시저로 개발한다고 하자.
- 프로시저쪽 코드도 수정
- 애플리케이션쪽에 호출하는 소스코드도 수정
- 컴파일, 배포 등 다시 다해야함
- 따라서, 유지 관리 보수 비용이 커진다는 단점 발생
# DB서버 추가
ex) Presentation tier에서 온 트래픽 증가 시 DB 서버 추가하는 경우
- Logic tier 서버의 CPU 사용량보다 Data tier의 DB 서버 CPU 사용량이 훨씬 많을 것
- 이런 상황에서 Traffic이 갑자기 폭발적으로 증가하면?
- 프로시저를 통해 비즈니스 로직을 다 가지고 있으니까 DB 서버 하나로 감당하기 힘들 것
- RDBMS에 부하가 몰려서 신규 DB 서버를 추가했다고 하자.
- 추가해봤자, 바로 해결이 안된다. 데이터는 기존 서버에만 있고 새로운 서버에는 똑같이 데이터를 복제해줘야 하는데… 언제 데이터를 전부 복사하겠나?
- 비즈니스 로직을 프로시저를 통해서 RDBMS에 두게 된다면, DB 서버의 CPU 사용량이나 메모리 사용량이 늘어나게 되고, 트래픽이 폭발적으로 증가한 유사시에 긴급하게 대응이 불가능하게 된다. DB 서버를 추가하는 것은 간단하지 않다.
# transparent 보장X
ex) stored procedure의 이름을 바꾸는 상황이라고 하자
- 이렇게 바로 이름을 바꿔버리면? 기존에 애플리케이션 서버에서 호출하던 프로시저의 이름과 다르기 때문에 바로 서비스에 문제가 생긴다.
- 이렇게 이름 바꿀 프로시저를 새로 만들고 기존에 애플리케이션 서버에서 호출하던 프로시저의 이름을 하나 바꾸고 재가동, 하나 바꾸고 재가동, … 이 짓을 반복한다.
- 마지막으로 기존의 프로시저를 삭제하는 것이다.
- 오히려, transparent 하기는 커녕, 소스 코드에 로직이 있는 경우보다 더 손이 많이 갈 수 있는 단점이 있다.
# transparent 항상좋음?
ex) stored procedure의 body 부분만 바꾸는 상황이라고 하자
- body 부분만 바꿔서 번거롭게 애플리케이션 서버의 프로시저 호출 부분을 바꾸지 않고 transparent하게 변경할 수 있다.
- 그런데, 바뀐 부분에 예상치 못한 버그가 생겨서 난리가 나게 되었다.
- 다시 이전으로 롤백한다고 하더라도, 버그가 있는 프로시저가 동작하고 있었던 동안에 이와 관련된 모든 트래픽들은 안좋은 영향을 받았을 것이다.
# 양날의 검 재사용
ex) 하나의 서비스에서 요청하는 트래픽이 폭발적으로 증가한다고 하자.
- 스프링 쪽 서비스 A에서 폭발적으로 요청한다면 나머지 다른 서비스도 영향을 받게 됨
- 이렇게, 여러 서비스가 동시에 DBMS의 프로시저를 사용하게 되면, 통제되지 않는 사용으로 모두에게 문제가 발생할 수 있게 된다. stored procedure를 재사용 가능하다는 것은 양날의 검이 되는 것이다.
- 그래서 이런 식으로 아키텍처를 가져가는 것이 좋다.
- DB의 앞단에 데이터를 관리하는 서비스를 하나 만든다.
- 실제 서비스들은 Data Service를 통해 원하는 로직을 사용할 수 있도록 한다.
- Data Service는 Restful API 같은 것을 통하여 인터페이스를 제공해야 한다.
- 각각의 서비스들은 API를 호출하는 형태로 사용하는 것이다.
- 이런 상황에서 Service A의 트래픽이 폭발적으로 증가하면, Data Service 쪽에서 Service A에 해당하는 호출만 막으면 된다.
- 이렇게 하면 Service B와 Service C는 문제가 없게 된다.
그런데, 이렇게 하면 Data Service 쪽에서는 고민이 된다.
- 프로시저를 서비스 A, B, C에서 재사용하면서 사용하는 것이 아니라 Data Service에서만 사용하기 때문이다.
- 그래서 Data Service 쪽을 관리하는 사람은 그냥 프로시저를 쓰지 말고 소스코드로 관리하자는 쪽으로 가게 되는 것이다.
# 완벽한 정보제한 불가
사실 위에서는 간접적으로 정보에 접근하도록 하여 민감한 정보를 감췄다고는 하지만..
- 이렇게 return 값을 받아버리면 적어도, 개발자들은 민감한 정보에 대한 접근이 가능해진다.
- 또, 이렇게 DB 혹은 테이블 접근을 막으면 개발 및 CS (Customer Service) 업무의 신속함이 떨어진다.
그래서, 사실 보안과 관련된 부분은 아래와 같이 하는 것이 좋다.
- 담당자나 개발자에게만 DB 혹은 테이블 권한을 부여하자
- 민감한 정보는 암호화해서 저장하자
- 보안서약서 등을 통해 정책적으로 보안을 강화하자
# 이외의 단점
- procedure로는 복잡하고 유연한 코드를 작성하기 어렵다.
- 오늘날의 프로그래밍 언어는 훨씬 다양하고 강력한 기능들을 제공한다.
- 굳이 프로시저를??
- procedure는 가독성이 떨어진다.
- procedure는 디버깅이 어렵다.
# Logic tier 장점
그렇다면, 프로시저를 쓰지 않고 Logic tier에서 비즈니스 로직을 관리하는 방식은 어떤 면에서 좋다는 말일까? 사실 프로시저의 장점에서 언급했던 것을 로직 티어에서 관리하면 더 좋게 해결할 수도 있다.
- 트래픽이 증가했을 때, 서버 추가가 쉽다.
- 하나씩 재가동하는 불편한 방법이, 버그 발생에 관한 이점을 가져온다.
- 응답속도 향상은 사실 가능하다.
# 서버추가 쉬움
프로시저의 단점에서 DB서버 추가가 힘들다는 것이 있었는데, Logic tier에서는 애플리케이션 서버 투입이 간단하다.
- 애초에 트래픽을 분산받아서 사용하기 떄문에 좀 더 좋은 상황
- 최근은 클라우드가 잘 되어있어서 오토 스케일링(auto scaling) 같은거 이용하면 애플리케이션 서버 추가하는 것은 매우 쉽다.
- Logic tier에 애플리케이션 서버를 투입하는 것은 간단하다. 데이터를 가지고 있지 않기에 그냥 추가하면 된다. 따라서, CPU 혹은 메모리 부하를 쉽게 분산시킬 수 있다.
# 버그 피해 최소화
프로시저의 단점에서 transparent가 항상 좋은 것이냐는 것이 있었는데, Logic tier에서의 하나하나 재가동 하는 부분 덕에 오히려 좋은 점도 있다.
- 하나 하나 재가동 해야하니까, 하나 바꾸고 모니터링 하고 있는다.
- 이렇게 버그가 발생한걸 발견하면, 간헐적으로 문제가 발생했다는 것을 확인하니까 빠르게 이전 버전으로 롤백할 수 있다.
- 물론, 이 순간에 이 서버로 들어온 트래픽은 안좋은 영향을 받았을 것이다.
- 하지만 stored procedure 때는 온전히 모든 서버가 안좋은 영향을 받았다면,
- 이번에는 4분의 1에 해당하는 서버만 안좋은 영향을 받았을 것이다. 그나마 훨씬 낫다.
- 이렇게 애플리케이션 서버를 통해 배포를 매번 하는 것은 번거롭기는 해도 예상치 못한 버그의 영향을 최소화 할 수 있다.
# 응답 속도 감소
프로시저의 장점에서 응답속도 감소를 말할 때, Logic tier에 비즈니스 로직을 둔다면 응답속도가 느리다고 했는데, 사실 Logic tier에 비즈니스 로직을 두고도 응답 속도를 향상시킬 수 있다.
- 각각의 쿼리문 (
select
,insert
,update
)이 매번 요청을 보내서 응답속도에 문제가 있다고 했었다. - 그렇다면 만약에, insert와 update를 동시에 한다면?
- 스레드 풀(Thread Pool)을 사용하던 Non-blocking IO를 사용하던 동시에 DB서버로 요청을 보낸다.
- 순차적으로 요청할 필요가 없는 경우라면, ( 동시에 진행이 가능한 경우라면 ) 동시에 호출하여 응답속도를 향상시킬 수 있다.
ex) 그런데, 예를 들어 순차적으로 요청해야 하는 경우라면 어떻게 응답 속도를 향상시킬까?
- select문 A와 B는 반드시 순차적으로 실행되어야 하는 상황이라고 하자.
- 이 경우에는 어떻게 응답 속도를 향상시킬까?
- 이렇게 캐시(cache)를 사용한다. 여기서 사용된 캐시는 redis이다.
- 소스코드에 캐시 관련 로직도 추가한다.
- 캐시가 추가되면서 타임라인에도 변화가 생긴다.
getPoint(int id)
라는 로직은 id를 받으면 그 id의 포인트를 가져오는 로직이다.getFromCache(id)
는 id에 대한 포인트가 캐시에 있는지 먼저 확인- 초기에는 당연히 캐시에 아무것도 없기 때문에
return point if not null
수행하지 않고 밑으로 코드 실행 - 2번의 select문 수행 이후 계산하여 얻은 포인트를 캐시에 넣는다.
- redis는 key-value 형태로 데이터를 저장함
- 얼마동안 캐시에 저장할 것인지 life time도 저장함. 여기서는 60초라고 해놨음
- 이후 계산한 포인트를 반환
- 당장은 네트워크를 좀 더 타게 되어서 복잡해진 것처럼 보임. 하지만..?
- 같은 id에 대해서는 캐시에 있는 동안 한 번만 왔다갔다 하니까 응답 속도가 향상됨!!
- 이런식으로 비즈니스 로직을 소스코드에 두고도, 캐시를 사용하면 응답 속도를 향상시키면서 DB 부하까지고 줄이는 엄청난 효과를 가져온다. 실무에서 정말 많이 사용하는 패턴