서버개발자가 되는법 - #10 배치 프로그램, Django에서 배치만들기

     

목차 - 2020/09/29 - [Study/서버] - 서버개발자가 되는법 - 목차

 

git - tkdlek11112/server_dev at 서버개발자_10 (github.com)

 

유튜브 - 

 

 

들어가기 전에

 

배치(batch)프로그램이라는 말을 회사에서 처음 들었을 때는 '뭘 배치한다는 거지? 뭔가 흩어져 있는 것들을 정렬해주는 프로그램인가?'라는 생각을 했었던 것 같습니다. 배치라는 말이 뭔가 한글 같은데 사실은 영어입니다.

 

배치 = 일괄

 

한국어로 번역하면 '일괄'이라는 뜻인데, 뭔가 작업을 일괄적으로 처리할 일이 있을 때 배치 프로그램을 사용합니다. 보통 줄여서 배치라고 하죠. 회사에서 '배치 잘 돌았어?'라는 말을 많이 하게 되는데, 배치 프로그램이 잘 실행되어서 정상적으로 작업이 끝났는지를 물어보는 말입니다. 왜 프로그램이 돌았다고 하는지는 모르겠는데, 아마 시스템이 작업을 할 때 프로그래스 바가 빙글빙글 도는 걸 보고 잘 돌았냐고 말하는 게 아닐까 싶네여 ㅋㅋㅋ

 

아무튼 배치프로그램은 뭉탱이로 뭔가 많은 작업을 해야 할 때 사용합니다. 사실 배치가 어떻게 실행되는지 보다는, "어떤 작업을 배치프로그램으로 만들어야 하나?"가 중요한 것 같습니다. 가장 흔한 배치 프로그램은 밤 12시가 되면 시스템의 날짜가 다음날로 넘어가면서 그에 따라 실행되어야 하는 프로그램들을 예로 들 수 있습니다. 

 

배치 프로그램에 대해 이론적으로 이것저것 설명을 먼저 하기보다는, 코드로 만들어보면서 설명하도록 하겠습니다.

 

 

 

 

Django에서 배치프로그램 만들기

 

사실 배치프로그램이라고 해서 따로 특별하게 무언가를 만들어야 하는 것은 없습니다. rest api를 이용해서도 배치 프로그램을 만들 수 있으니까요. 정해진 시간에 정해진 작업을 한다면 어떤 방법으로 만들어도 상관은 없습니다. 아마 프레임워크마다 배치 프로그램에 대한 라이브러리를 제공해주기도 하고, 만들어야 하기도 합니다. Django에서는 보통 Command기능을 이용해서 배치 프로그램을 만듭니다. 

 

Django에서 command는 python manage.py <command>로 사용하는 기능들입니다. makemigrations나 migrate가 command의 종류인데, 이런 command들을 custom하게 만들어서 사용하면 쉽게 배치 프로그램을 만들 수 있습니다. 이 custom command를 만들기 위한 공식 문서는 아래 링크.

Writing custom django-admin commands | Django documentation | Django (djangoproject.com)

 

 

기존에 했던 프로젝트에서 간단한 custom command를 만들어보겠습니다.

<APP>/management/commands/<yourcommand>.py

일단 Django에서는 command.py의 경로가 제일 중요한데, 특정 앱안에 management폴더를 만들고, 그 안에 commands폴더를 만들고, 그 안에 command.py 소스파일을 만들어야 합니다. 아마 장고 프레임워크 자체에서 각각의 앱 폴더 안에 management/commands 폴더를 command로 인식하고 있는 것 같습니다. 저는 common/management/commands/testcommand.py를 만들었습니다. 이때 주의할 점은 command.py가 위치한 앱이 INSTALL_APP에 포함되어있어야 한다는 점입니다.

 

# settings.py
INSTALLED_APPS = [
    ...
    'common',
    ...
]

 

등록이 안되어있으면 경로를 인식 못하는지 command를 못 찾더라고요. 제일 중요한 testcommnad.py를 만들어야 하는데 간단하게 아래와 같이 만들었습니다.

 

# common/management/command/testcommand.py
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    help = 'test'

    def handle(self, *args, **options):
        print("테스트 배치 프로그램 실행")

 

custom command를 만드는 방법은 Django에서 제공하는 BaseCommand를 상속받는 Command Class를 만들면 됩니다. 그리고 실행하고자 하는 코드를 내부에 def handle안에 적어주면, command를 실행했을 때 handle이 실행되면서 우리가 입력한 코드가 실행됩니다. 이 command.py는 실행하면 print 한번 하고 종료되는 custom command가 되었습니다. 이제 터미널에서 python manage.py testcommand를 입력해보겠습니다.

 

 

심심해서 두 번 입력했습니다. 실행시키면 정말 간단하게 우리가 입력한 문구만 출력하고 끝납니다. 배치 프로그램은 이런 느낌입니다. 자기가 할 작업하고 알아서 종료됩니다. 

 

이제 실제 느낌의 배치를 만들기 위해서 기존에 사용했던 ToDo 테이블을 사용해보겠습니다. todo앱을 만들기 위해 db.sqlite3에 있는 task테이블에 사용자의 todo를 관리하고 있었습니다. 

 

todo앱을 위해 사용했던 task 테이블

 

데이터가 많이 없으니까 좀 채워줍니다. 배치로 만들어볼 시나리오는 현재 시간 이전에 끝내야 했던 Todo앱의 상태(state) 값을 3으로 바꿔주는 작업입니다. state=3이면 "끝내야 하는데 아직 안 끝냈다"라는 상태 값입니다. 그렇게 사용하기로 제가 정의한 거지 꼭 3의 값은 아니어도 됩니다 ㅎㅎ

 

테스트를 위해 end_date값을 채워줌

배치 테스트를 위해 end_date에다가 오늘 날짜(6월 13일) 기준으로 과거와 미래로 반반씩 세팅해주었습니다. sqlite3에서 그냥 더블클릭해서 시간을 정하니까 이상하게 들어가서 query를 사용해서 하나하나 업데이트해주었습니다.

 

update task set end_date = strftime('2021-06-15') where id > 10;
update task set end_date = strftime('2021-06-01') where id <= 1;

 

요론식으로 ㅋㅋㅋ

 

이제 배치 프로그램에서 task테이블에 있는 todo 중에 오늘날짜보다 이전인 값들은 state를 3으로 바꾸게 해 보겠습니다.

# common/management/commands/testcommand.py
from django.core.management.base import BaseCommand
from todo.models import Task
from datetime import datetime


class Command(BaseCommand):
    help = 'test'

    def handle(self, *args, **options):
        task_list = Task.objects.all()

        for task in task_list:
            if task.end_date < datetime.now().date():
                task.state = 3
                task.save()
                print('ToDo :', task.name, '의 완료날짜(', task.end_date, ')가 지났습니다.')

 

handle안에 내용을 보면 task테이블에 있는 모든 값을 가져오고 나서, 하나씩 날짜를 비교하면서 end_date가 오늘보다 작으면 state를 3으로 바꾸고 저장하게 되어있습니다. 그리고 뭔가 진행되고 있는 사항을 알 수 있게 print문으로 상태 값을 출력하도록 만들었습니다. 이제 이 command를 실행해서 우리가 원하던 작업이 실행되는지 테스트해봅시다.

 

 

배치 프로그램 실행 결과

print를 그럴듯하게 찍어놔서 뭔가 있어 보이게 배치 프로그램이 실행되었습니다. 실제로 DB에 값도 바뀌었는지 확인해봅시다.

 

state값이 3으로 변경된것을 확인

 

 

배치 프로그램은 언제 사용하는가?

 

배치는 이렇게 특정 시간에 일괄적으로 값을 변경하거나 생성하는 작업에 적합한 서비스입니다. 보통 서비스마다 다르기는 하지만 배치 프로그램이 실행되는 시간은 몇 분단위에서 몇 시간까지 걸리는 프로그램도 있습니다. 특히 금융권 같은 경우 날짜가 바뀌게 되면 전날 처리되었던 여러가지 데이터를 분석하고 합치고 처리하는 여러 배치프로그램이 실행됩니다. 카드나 은행에서 밤 12시부터 새벽 1시까지 카드결제나 이체같은 서비스가 안되는 것을 경험해보신적이 있으실텐데, 바로 이 시간들이 서버에서 배치프로그램이 도는 시간입니다. 요즘엔 서버 사양이 좋아져서 30분~1시간까지 배치가 도는 일을 없긴 한데, 아직도 많은 회사들이 몇 시간짜리 배치 프로그램을 사용하고 있습니다.

 

말한 것처럼 배치 프로그램은 작업시간이 상~~당히 긴 서비스로 인식되고 있기 때문에 기능을 정의할 때 배치를 사용할 것인지 사용하지 않을 것인지 결정해야 되는 일이 많습니다. 그런데 앞에 금융권을 예를 들어보면 배치를 꼭 사용해야만 할 것같은데 사용하지 않을 수 도 있을까요?

 

최근 프로젝트를 하면서 느끼는 점이지만, 배치 프로그램에 의존하게 되면 서비스의 실시간성이 상당히 떨어집니다. 예를 들어서 우리가 만든 testcommand 배치프로그램은 밤 12시에 오늘보다 이전에 끝나야 했던 todo들의 상태를 3으로 바꿔주는 역할을 합니다. 지금은 데이터가 쪼금이라서 1초도 안 걸리고 실행이 되지만, 만약 체크해야 하는 데이터가 무수히 많아져서 1시간이 걸린다고 생각해봅시다.

 

 

1시간 동안 모든 사용자에 todo리스트를 순차적으로 체크하기 때문에 맨 마지막에 작업이 되는 사용자의 경우 새벽 1시가 지나야 todo의 상태 값이 변한 것을 확인할 수 있습니다. 실제로는 24:00가 지나고 나서 상태가 3이어야 하는데 1시간이 지난 01:00에서야 확인할 수 있는 것이죠. 이렇게 배치 시간이 오래 걸리게 되면 실시간 서비스가 불가능하게 됩니다. 따라서 실시간으로 서비스가 필요한 경우 배치 프로그램보다는 다른 방법을 사용하는 게 일반적입니다. 

 

만약 동일한 기능을 배치 프로그램이 아니라 다른 방법으로 해결하려면, Todo를 조회할 때 end_date와 현재 시간을 실시간으로 비교해서 상태 값을 만들어주면 됩니다. Todo의 상태값을 AP에서 처리해주는 방법이죠.

 

 

실시간 vs 배치

서비스를 기획하다 보면 언제 실시간으로 처리해야 하는지, 배치를 사용해야 하는지 고민하게 됩니다. 최근에 공부 타이머 앱을 만들고, 자신이 공부한 내역을 한 달치를 불러와서 매일 얼만큼 공부했는지 보여주는 서비스를 만들었습니다. 이 서비스에 대해서는 실시간으로 만들어야 할까요 아니면 배치로 만들어야 할까요?

 

일단 저는 배치를 이용했습니다. 24:00가 지나면 전날 사용자가 공부한 시간을 날짜로 기록해서 일별 공부 테이블을 만들었습니다. 따라서 사용자가 매일 얼만큼 공부했는지 날짜별로 조회할 수 있습니다. API를 만들 때는 일별 공부 테이블에서 한달치를 고대로 가져와서 보여주기만 하면 됩니다.

 

일별 테이블을 이용한 관리

이렇게 하면 API에서 단순히 테이블을 조회해서 값만 넘겨주면 되기 때문에 매우 빨리 처리됩니다. 

 

만약 이 서비스를 실시간으로 처리하도록 만든다면, 사용자의 공부 기록을 전부 가져와서 날짜별로 공부한 시간은 sum해준 다음 클라이언트에 전달해야 하기 때문에 처리하는 시간이 상대적으로 많이 소모됩니다. 따라서 API가 느려지고, DB에 조회하는 데이터 양도 많아지게 됩니다.

 

그럼 배치로 하는 게 무조건 좋은 거 아닌가요?

 

라고 생각했었는데, 약간의 문제가 있습니다.

 

먼저 24:00가 되면 전날에 대한 사용자의 공부를 확정 짓기 때문에 나중에 사용자가 공부한 시간을 수정한다면, 일별 테이블을 고쳐줘야 합니다. 예를 들어 지난주에 공부했던 1시간을 지우게 되면, 일별 테이블에서 그날의 공부시간에 1시간을 빼줘야 합니다. 하지만 실시간으로 만들었다면, 어차피 전체 공부시간을 가지고 실시간으로 일별 데이터를 만들기 때문에 1시간을 지운 게 자동으로 반영돼서 서비스가 됩니다.

 

이처럼 배치를 통해 새로운 일별 데이터를 만들었기 때문에, 원데이터가 수정이 되면 당연히 새로 만든 데이터도 동기화를 해주어야 합니다. 이 일련의 과정이 견고하게 되어있지 않다면 자칫 잘못된 데이터로 서비스가 될 수 있습니다. 데이터베이스 정규화에서도 알 수 있듯이, 데이터는 가급적 중복되어서 저장되면 관리가 힘들어지게 됩니다. 

 

이 문제 말고도 여러 가지 문제가 발생해서 배치로 만들었던 서비스를 다시 실시간으로 돌려야 하는 상황이 되었습니다. 처음에 기획할 때부터 이런 문제들을 고민해봤다면 배치로 안 만들었을 텐데... 개발에서 분석 단계가 중요한 이유가 바로 이겁니다. ㅎㅎ

 

 

이렇게 배치와 실시간. 둘 중에 어떤 것을 사용하느냐는 서비스의 특징에 따라서 정해야 합니다. 만약 과거 데이터에 대한 수정이 절대 일어나지 않는 서비스라면 배치를 사용해도 상관없습니다. 하지만 추후에도 계속적으로 변화가 있는 데이터라면 실시간이 좋습니다. 만약 서비스의 속도가 최 우선시된다면 데이터가 변하더라도 배치를 사용해서 데이터를 생성하는 것이 좋습니다. 한 달이 아니라 1년의 데이터를 모아서 한 번에 보여줘야 한다면, 1년 치 데이터를 이용해 미리 결과값을 뽑아놓는 배치를 만들어 놓는 것이 합리적인 방법입니다.

 

 

 

배치 프로그램은 어떻게 실행되는가?

배치 프로그램을 만들었으면 누군가가 실행을 시켜줘야 하는데, 보통 스케쥴러라고 부르는 녀석이 배치 프로그램의 실행을 담당하게 됩니다. 스케쥴러는 지정된 시간에 특정 프로그램을 실행시켜주는 프로그램을 지칭하는데 대표적으로 linux에 crontab이 있습니다. 

 

# crontab
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command
0 5 * * 1 python manage.py testcommand

 crontab은 대충 위처럼 생겼습니다. 앞에 0 5 * * 1 이 몇 시에 어떤 주기로 프로그램을 실행할 건지를 나타내는 기호입니다. 공식 문서는 crontab(5) - Linux manual page (man7.org) 요기입니다. 간단하게 설명하면 앞에서부터 분, 시간, 일, 월, 요일입니다. 0 5 * * 1이라고 적혀있는 건 매주 월요일 5:00에 python manage.py testcommand를 실행해라라는 뜻입니다. 이렇게 crontab을 사용할 수도 있고, 우리가 많이 들어본 jenkins를 사용할 수도 있습니다.

 

 

우리에게 친숙한 jenkins

 

보통 jenkins를 CI/CD툴이라고 생각해서 배포만 하는 툴로 생각할 수 있는데, jenkins도 훌륭한 스케쥴러입니다. crontab과 지정된 시간에 특정 쉘을 실행시킬 수 있고, 관리자 페이지를 통해 정상 실행 여부까지 알 수 있습니다.

 

 

 

 

정리하면서

이번 포스팅에서는 배치 프로그램에 대해 알아봤습니다. Django에서 간단한 배치 프로그램을 만들고 실행시켜보았고, 언제 배치프로그램을 만들어야 하는지를 생각해봤습니다. 실제로 업무에서 "이걸 배치로 만들어야 하나?"라는 고민을 굉장히 많이 하는 것 같은데, 사실 배치로 하면 작업이 조금 쉬워지는 면이 있지만, 서비스 자체가 부드럽지 않게 돌아가게 될 수도 있습니다. 하지만 그럼에도 불구하고 배치를 이용해서밖에 못 만드는 서비스도 존재하기 때문에 서비스의 특징을 잘 생각해서 어떤 것을 사용할지 고민해야 합니다. 

 

배치 프로그램에 대해 설명하면서 스케쥴러인 crontab과 jenkins를 살짝 언급했는데, jenkins에 대한 내용은 다른 포스팅에서 다뤄보도록 하겠습니다.

 

그럼 배치에 대한 내용은 여기까지~

 

 

 

 

반응형

댓글

Designed by JB FACTORY