본문 바로가기

소프트웨어개발자의 삶/실전기록

프로시저에 업무로직(비지니스로직)을 넣으면 안되는 이유

프로시저에 업무 로직을 넣으면 안되는 이유

OKKY에 올라온글에 댓글을 달 여유가 조금 생겼다.

프로그램 소스코드에 로직을 넣는게 맞느냐 DB레벨에 있는 프로시저에 로직을 넣는게 낫느냐. 각각의 장단점을 질문하는 글이 올라 왔었다. IT개발자들은 조심스럽다. 그래서 다들 각각의 장단점이 있다는 식으로 덧글이 조금씩 있었다. 하지만 대체적으로 DB프로시저에 로직을 다는 것에 대해서 꺼려하는 듯한 글이 달렸다. 장점으로는 서버를 재배포하지 않아도 된다는 것들이 주를 이루었다. 나도 덧글을 달아보았다. 

프로시저를 사용하는 장점은 속도이다. DB레벨에서의 속도에서 프로시저가 가장 빠르다. 데이터베이스는 SQL을 실행하면 나름 해석이라는 과정을 거친다. 그리고 데이터를 어떻게 긁어올지 판단을 하게 된다. 프로시저는 이런 해석의 과정이 컴파일이라는 과정을 통해서 정해져버린다. 그래서 그런 해석과정이 없어서 매우 빠르게 작동한다. 이것이 프로시저가 가자는 최고의 장점이다.

하지만 그 장점을 제외하고 만약 프로시저에 로직을 구성하게 되면 모든 것이 단점으로 다가온다.

프로시저에 로직을 넣으면 만나게 되는 문제점

1. 긴 로직의 경우 소스코드 이해를 하기가 힘들다.

 프로시저의 문법은 구조적인 문법이며 순차적인 문법이다. 객체지향의 문법을 이용하여 작성할 경우 사람이 읽기 쉬운 코드를 얼마든지 만들 수 있다. 하지만 프로시저는 문법으로는 그 한계가 존재한다. 객체지향 문법을 이용하여 클래스간의 역활을 나눌 수도 없으며 가시자를 부여하여 외부에서 호출 되는일 내가 호출 할 일 등으로 분리할 수도 없다. 일단 언어적으로 프로그래밍 언어와의 한계가 명확하기 때문에 절대 랭귀지 측면의 문법의 다양함을 따라할 수가 없다.

2. 테스트가 힘들다

 프로시저는 DB에 있는 자료를 기반으로 돌아가는 코드이다. 고로 데이터도 데이터를 구성해서 해야된다. 이 데이터는 테이블에 실제 존재해야 되는 데이터이다. 문제는 단일 테이블의 경우는 어떻게 자료를 구성하면되는데 보통 복잡하게 프로시저에 로직을 넣게 되면 여러 테이블의 데이터를 참조하게 된다. 그러면 이런 여러 테이블의 데이터를 구성해줘야되는데, 만약 관계라도 설정되어있으면 테스트를 위한 데이터 구성에서부터 많은 애를 먹게 된다.

3. 소스코드가 관리되지 않는다.

 프로시저는 작성한 코드를 실행해버리면 원래 소스가 바뀌어 버린다. 이전의 코드를 별도로 저장하는 스스로의 노력이 없으면 소스코드 관리가 되지 않는다. 랭귀지는 소스관리 프로그램등을 이용하여 소스코드의 히스토리가 분명하게 관리되도록 할 수 있다. 하지만 이것이 불가능해지는 것이다. 습관적으로 실행을 눌러 이전 소스를 복귀하는데 애를 먹는 경우를 안 경험해본 개발자가 없을 것이다.

4. 무결성 보장이 제한적이다.

 데이터베이스에 존재하는 데이터베이스 수준에서는 격리가 되어 무결성이 보장되지만 데이터 베이스를 넘어서서 무결성을 보장하는 방법을 찾을 수 없다. 하지만 프로그래밍에서는 여러 DB 혹은 다른 서비스와 함께 무결성 보장을 흉내라도 내볼 수 있다.

5. 성능향상에 매우 많은 비용과 노력이 든다.(분산처리가 매우 힘들다.)

  데이터베이스는 ScaleOut이 매우 힘들다. WAS 는 ScaleOut이 로드밸런서만 앞에 있어주면 같은 WAS를 여러개 둠으로서 확장해 나가기 쉽지만 DB는 최종적이고 불가역적인 데이터의 저장 부분이 이기 때문에 여러대의 DB가 같은 데이터를 무결함게 남기기 위한 기술을 적용하기가 매우 까다롭다. 만약 내가 만든 프로시저가 많은 성능을 요구한다면 어떻게 ScaleOut을 할 것인가? 보통은 ScaleUp 하는 방법으로 갈 수 밖에 없다. 기껏해야 나눌 수 있는게 용도별로 DB를 나누는 것이다. 이는 또다른 백업과 관리의 노력이 드는 것과 같다. 여러대의 WAS를 늘려가는 것과 용도별로 다른 DB를 늘려가는 것은 전혀 차원이 다른 문제이다.

어떠한 대한이 있을까? (나의 후회와 경험)

 도시가스 요금계산을 프로시저로 개발을 했었다. 당시 프로시저로 개발을 할때 프로시저 개발을 반대하는 유일한 사람이나였다. 우리게는 유능한 DBA가 없었고 그나마 프로시저로 가장 좋은 성능을 뽑아낼 수 있었던 것도 미천하지만 나 밖에 없었다. 나는 프로시저가 읽기 어렵다는 이유로 반대를 했었다. 앞 프로젝트에서 유지보수 할 때 마자 몇 천단위가 넘는 로직을 찾아다니며 수정을 하고 가슴을 졸였던 기억을 하니 프로시저로 하지 말았으면 하는 생각이 엄청났다. 결국 그 프로젝트도 속도 이슈 때문에 프로시저보다 빠른 방법을 찾을 수 없었기 때문에 수긍하고 프로시저로 개발을 하였다.

 하지만 돌이켜보니 정말 잘 못 된 선택이였다. 프로그램은 개발보다 유지관리에 많은 비용이 든다. 프로시저에 로직을 넣기 시작하면 앞에서 나열한 저런 이유 때문에 비용이 기하급수적으로 증가하는 흐름을 피할 수가 없다. 기껏 피해봐야 소극적인 대응으로 가는 수 밖에 없다. 가급적이면 안 바꾸려고 노력하는 것이고 어쨌든 피해보려고 노력하는 것이다. 하지만 꼭 바꿔야되는 상황을 맞이하게 되면 진짜 심호흡 한번 하고 들이대야 되는 상황이 온다.

 만약 내가 다시 개발을 한다고 하면 어떻게 할 것인가? 많은 고민을 해보았다. 그리고 가만히 생각해보면 그 속도 문제에 너무나 신경을 쓰고 있다는 것이였다. 속도 이슈가 발생하는 이유는 대량으로 작업 처리가 일어나야 된다는 것이다. 보통 도시가스는 검침이라는 행위가 일어나면 그 검침값을 입력을 받는다. 그리고 나면 지난달의 검침과 이번달의 검침 값의 차이가 발생하게 된다. 그 차이 값이 사용량이 되는 것이다. 그 사용량을 기반으로 요금제를 적용해야되는 상황에 맞추어 요금을 계산하면 된다. 그리고 문제인 연체료 계산이 남는다. 당월의 요금계산은 그렇게 많은 데이터가 요구되는 것이 아니지만 연체료나 정산금액 등을 위해서는 지난 요금을 모두 뒤져야되는 상황이 온다. 그렇게 그런 데이터를 뒤져서 하나의 고지데이터가 만들어지게 된다. 물론 수납은 또다른 더 거대한 로직을 만나게 되지만....

 하나의 요금계산은 그 데이터가 많다고 하여도 금방 처리할 수 있는데, 문제는 도시가스요금은 규모에 따라서 다르지만 일단 많은 세대의 요금을 회차를 나누어 한번에 쫙 돌려서 계산을 해줘야 한다.(여기서 속도 이슈가 발생한다.) 많은 세대의 요금 계산을 돌리기 위해서는 각 세대별로 저런 데이터 처리를 해야되니 몇곱절의 데이터가 긁혀 나와야되는 것이다. 수천만, 수억건의 데이터가 긁힐 수 밖에 없다. 이때문에 프로그램으로 처리해서는 데이터를 가져오고 계산을 하고 하는 등의 처리가 빡세질 수 밖에 없는 것이다. 

 하지만 가만히 생각해보자. 요금계산이 꼭 버튼 한번을 클릭하고 나면 다 돌아야되는가? 보통 그렇지는 않다. 저녁에 퇴근하며 돌리고 가면 아침에 다 돌아가있으면 되는 수준이다. 그렇다면 최소 12시간의 시간은 확보가 될 수 있다는 것이다. 저녁에 돌아간 다음에 문제가 발생할까봐 근무시간 중에 돌리고 확인한다고 치자. 그렇다고해소 7~8시간이 확보가 된다. 그 안에 돌아가면 되는 것이다.

 내가 다시 요금계산이 도는 프로그램을 개발한다면 이 요금계산이 필요한 시점에 클라우드의 많은 자원을 하당하여 병렬로 계산하도록 할 것 같다. 네트워크 통신이 엄청나게 발생할 것이다. 만약 중복되는 데이터의 전송이 많아진다면 중간에 데이터를 관리하는 무엇인가도 만들 것 같다. 하지만 내가 마주했던 요금계산에서는 1세대의 데이터가 중요했다. 1세대의 데이터를 계산하는데 마스터성 데이터 말고는 어떤 다른 세대의 데이터도 필요하지 않았다. 각각의 요금계산처리 프로그램이 10,000세대씩 계산을 하고 빠진다면 어떨까? 쿠버네티스 환경에서 돌아가는 인프라라면 요금계산 로직프로그램이 매월 특정 주기만 되면 ClonJob으로 100개가 돌아간다면? 100만세대는 계산이 될 것이다. (보통의 도시가스는 차수를 나누어 관리하기 때문에 100만세대를 한번에 같은 날짜에 계산하는 곳은 없다고 알고 있다.)

 그리고 그렇게 개발된 요금계산로직은 다시 WAS에서도 재사용이 가능한 형태로 만들 수 있을 것이다. 이 로직은 다시 민원에서 대응하기 위한 개별 계산도 가능해진다. 자바면 jar파일로 .net이면 dll로 만들 수 있을 것이다. 그러면 이 로직은 TDD를 적용하여 요금계산의 시나리오와 함께 같이 묶어서 요금계산이 바뀔때마다 정말 부담없이 테스트 시나리오를 만들어서 적용해 나갈 수 있을 것이다.

 거기까지 해보싶었는데 항상 그렇듯 나는 깊게 파며 개발하고 싶지만 회사는 나를 그냥 그렇게 놔두지 않는다. 하지만 매번 프로시저에 로직을 담아두는 바람에 실수를 연발하며 유지관리되는 시스템을 보면서 절대 프로시저로에 로직을 넣으면 안된다는 확실한 경험은 쌓게 되었다.

okky에 내가 남긴 댓글