권과장의 블로그
개발

수발주 업무 자동화, 직접 만든 사내 프로그램

매일 아침 거래처 사이트를 하나씩 열어 발주를 확인하던 업무를 자동화했습니다. 사이트마다 다른 화면을 어떻게 다뤘는지, 이틀을 헤맨 인증 문제의 진짜 원인은 무엇이었는지 적었습니다.

8분 읽기
수발주 업무 자동화, 직접 만든 사내 프로그램 썸네일
수발주 업무 자동화, 직접 만든 사내 프로그램 썸네일
권과장

낮에는 회사에서 일하고, 밤에는 코드를 만지고, 주말에는 부엌에 있습니다. 겪은 것을 정리해 두는 곳입니다.

목차

매일 아침 사람이 하던 업무를 프로그램에 넘겼습니다. 거래처 사이트에 로그인해서 오늘 들어온 발주가 있는지 확인하고, 엑셀을 받아 인쇄하고, 사내 수발주 시스템에 등록하는 과정입니다. 스케줄러가 매시 정각에 그날 발주 요일인 거래처만 골라 실행하고, 결과는 텔레그램으로 옵니다. 발주가 있으면 상세 내역이, 없으면 한 줄이 옵니다.

2026년 5월 말에 시작해서 9월인 지금도 계속 고치는 중입니다. 이 글은 완성된 프로그램 자랑이 아니라 중간 기록에 가깝습니다. 특히 이틀을 통째로 날린 문제 하나가 있는데, 그 이야기가 이 글에서 가장 쓸모 있는 부분일 것 같습니다.

참고로 제 업무가 아니었습니다. 옆에서 매일 아침 사이트를 대여섯 개씩 여는 걸 보고 만들었습니다. 거래처 이름은 밝히지 않고 A, B, C로 적겠습니다.

수발주 업무 자동화 프로그램의 실행 이력 화면

API가 없으면 남는 선택지는 크롤링뿐이다

처음에는 API를 찾았습니다. 데이터를 주고받는 정식 통로가 있으면 그게 가장 안정적이니까요. 사이트가 바뀌어도 API는 잘 안 바뀝니다.

문제는 거래처 사이트에 API가 거의 없다는 점이었습니다. 있는 곳도 유료였습니다. 발주 확인 하나 때문에 매달 비용을 내기는 어려운 상황이라 크롤링을 택했습니다. 사람이 브라우저에서 하던 클릭과 입력을 프로그램이 그대로 따라 하는 방식입니다.

크롤링을 고를 때 감수해야 하는 게 있습니다. 상대 사이트가 화면을 바꾸면 그날로 멈춥니다. 그래서 이 프로그램은 만들고 끝나는 게 아니라 계속 손봐야 하는 물건입니다. 시작하기 전에 이걸 알고 가는 편이 낫습니다.

기술 구성은 이렇습니다. 브라우저 조작은 Playwright, 백엔드는 Django, 화면은 Next.js, 데이터베이스는 PostgreSQL, 알림은 텔레그램입니다.

알아두기 크롤링 전에 해당 사이트의 이용 약관과 계약 조건을 먼저 확인하세요. 거래 관계가 있는 곳이라도 자동 접근을 어떻게 보는지는 따로 확인이 필요합니다.

사이트마다 이렇게까지 달랐다

여섯 곳을 붙이면서 알게 된 건, 웹사이트라는 게 겉만 비슷하지 속은 전혀 다르다는 사실입니다.

화면에서 글자를 긁을 수 없는 곳

A사는 오래된 화면 프레임워크를 씁니다. 입력창에 값을 넣는 일반적인 방법이 아예 안 먹혔습니다. 결국 화면 좌표를 찍어서 클릭하고, 키보드로 한 글자씩 타이핑하는 방식으로 돌렸습니다. 버튼도 마찬가지였습니다.

더 곤란했던 건 주문 목록이 그림처럼 그려진다는 점이었습니다. 화면에 글자가 보이는데 프로그램 입장에서는 읽을 수가 없습니다. 그래서 화면에서 데이터를 긁는 대신 엑셀 파일을 내려받아 파싱하는 쪽으로 방향을 바꿨습니다. 이게 오히려 안정적이었습니다.

여기에 사소하지만 오래 잡아먹은 것도 있었습니다. 로그인 영역이 화면 오른쪽 바깥에 있어서, 브라우저 창을 가로 1920 이상으로 띄우지 않으면 아예 보이지 않았습니다. 창 크기 때문에 로그인이 안 될 거라고는 상상도 못 했습니다.

팝업을 치우지 않으면 아무것도 안 되는 곳

B사는 로그인하자마자 별도 창이 네 개 뜹니다. 화면 안쪽 알림창도 여러 번 뜹니다. 이걸 치워 주지 않으면 다음 클릭이 전혀 먹지 않습니다.

그래서 로그인 직후에 뜨는 창을 모두 닫는 절차를 따로 뒀습니다. 사람이라면 무의식적으로 X를 누르고 넘어가는 동작인데, 프로그램에게는 하나하나 알려 줘야 합니다.

입력칸이 읽기 전용인 곳

C사는 날짜 입력칸이 읽기 전용이었습니다. 사람은 달력에서 고르면 되지만 프로그램은 값을 직접 넣을 수 없습니다. 읽기 전용 속성을 잠시 떼고 값을 넣은 뒤 엔터로 확정시키는 방법으로 풀었습니다.

날짜를 넣으면 달력이 떠서 그다음 칸 클릭을 가로막는 문제도 있었습니다. 입력하고 나서 ESC 키를 한 번 눌러 닫아 줍니다. 이런 건 어디에도 안 적혀 있고, 실행 화면을 보다가 발견하게 됩니다.

거래처마다 코드를 새로 짜지 않으려면

세 곳쯤 붙이고 나니 이대로는 끝이 없겠다 싶었습니다. 거래처가 늘 때마다 코드를 새로 짜면 관리가 안 됩니다.

그래서 방향을 바꿨습니다. 로그인하고 조회하는 절차 자체를 데이터베이스에 저장하기로 했습니다. 페이지 이동, 값 입력, 클릭, 드롭다운 선택, 대기, 인증번호 입력, 프레임 진입, 엑셀 다운로드, 새 창 진입 같은 동작을 순서대로 나열해 두면, 공통 실행기가 그 순서를 그대로 따라갑니다.

단계마다 실패했을 때 어떻게 할지도 함께 정해 둡니다. 거기서 멈출지, 그냥 건너뛸지, 세 번까지 다시 시도할지입니다. 팝업 닫기처럼 있을 수도 없을 수도 있는 동작은 건너뛰기로, 로그인처럼 실패하면 의미가 없는 동작은 중단으로 둡니다.

이렇게 하니 새 거래처를 붙일 때 코드를 건드리지 않아도 됩니다. 화면에서 단계를 등록하면 끝입니다. 다만 앞서 말한 A사처럼 도저히 일반화가 안 되는 곳은 전용 코드를 따로 뒀습니다. 모든 걸 하나의 틀에 넣으려다 틀이 무너지는 것보다 낫다고 판단했습니다.

사용자가 주소를 편집할 수 있게 하면 생기는 위험

여기서 놓치기 쉬운 게 있습니다. 방문할 주소와 파일 저장 경로를 사용자가 화면에서 편집할 수 있게 만들면, 그건 곧 서버에게 아무 데나 접속하라고 시킬 수 있다는 뜻입니다.

그대로 두면 서버 내부망을 훑거나 엉뚱한 경로에 파일을 쓰는 일이 가능해집니다. 그래서 사설망 대역과 내부 주소로는 못 가게 막고, 파일 저장은 지정한 폴더 밖으로 나가지 못하게 했습니다. 거래처 로그인 정보도 평문으로 두면 안 되니 암호화해서 저장하도록 바꿨습니다.

주의 사내용이라도 남의 사이트에 접속하는 프로그램은 외부 공격 통로가 될 수 있습니다. 편의 기능을 열 때마다 그게 무엇을 허용하는지 한 번 더 보시길 권합니다.

인증문자가 안 온다고 이틀을 헤맸는데 팝업이었다

이 글에서 가장 하고 싶은 이야기입니다. 2026년 8월 27일과 28일, 이틀을 여기에 썼습니다.

증상은 이랬습니다. A사 로그인 과정에 SMS 인증이 있는데, 문자가 3분에서 7분씩 늦게 오거나 아예 오지 않았습니다. 그런데 인증 화면은 3분이 지나면 끊깁니다. 사람이 직접 하면 잘만 오는데 프로그램으로 하면 안 왔습니다.

저는 원인을 이렇게 짚었습니다. 상대 쪽에서 자동화된 브라우저를 감지하고 발송을 후순위로 미루는 거라고요. 그럴듯했습니다. 그래서 브라우저를 진짜 크롬처럼 보이게 위장했습니다. 사용자 에이전트, 자동화 표시, 언어 설정, 그래픽 카드 정보, 플러그인 목록, 운영체제까지 손봤습니다.

안 됐습니다. 그리고 부작용까지 생겼습니다. 위장을 걸고 나니 이번에는 엑셀 다운로드가 되지 않았습니다.

실제 원인은 다른 데 있었습니다. 로그인 화면에 공지 팝업이 하나 떠 있었고, 그게 입력칸을 덮고 있었습니다. 클릭이 팝업에 막혀서 요청 자체가 제대로 안 들어갔던 겁니다. 팝업을 닫는 코드 한 조각으로 끝났습니다. 위장은 전부 되돌렸습니다.

이틀 동안 만든 걸 삼십 분 만에 지웠습니다. 여기서 배운 게 하나 있습니다. 원인을 그럴듯하게 추측하면 그 방향으로만 파게 됩니다. 감지당하고 있다고 믿는 순간, 저는 감지를 피하는 방법만 찾고 있었습니다. 화면을 한 번 열어 봤으면 첫날에 끝났을 문제였습니다.

인증번호를 사람이 안 쳐도 되게

인증 자체는 해결해야 할 과제로 남았습니다. 자동화를 만들어 놨는데 아침마다 사람이 문자를 보고 숫자를 쳐야 하면 반쪽입니다.

지금은 이렇게 돌아갑니다. 휴대폰으로 온 인증 문자를 텔레그램 봇으로 넘기고, 프로그램이 그 답장을 읽어서 입력합니다.

여기서도 걸린 게 있었습니다. 텔레그램이 알림을 밀어 주는 방식은 특정 포트만 허용하는데, 저희 환경에서는 그 포트를 쓸 수 없었습니다. 그래서 프로그램이 주기적으로 물어보는 방식으로 우회했습니다. 우아하진 않지만 잘 돌아갑니다.

도구끼리 부딪히는 문제

Playwright와 Django를 같이 쓰면서 겪은 것도 적어 둡니다. Playwright의 동기 방식이 내부적으로 특수한 실행 구조를 쓰는데, Django가 이걸 비동기 환경으로 오해해서 데이터베이스 접근을 막았습니다.

해결은 별도 작업 스레드에서만 브라우저를 돌리는 것이었습니다. 원인을 알기 전까지는 왜 데이터베이스가 갑자기 막히는지 전혀 감이 안 왔습니다. 오류 메시지만 봐서는 브라우저와 연결 지을 수가 없거든요.

AI에게 추측 대신 스크린샷을 남기게 했다

저는 개발이 본업이 아닙니다. 터미널에서 AI에게 상황을 설명하는 방식으로 만들었습니다. 그래서 AI가 무엇을 잘하고 무엇을 못하는지는 꽤 체감했습니다.

잘하는 건 분명했습니다. 사이트마다 다른 화면 구조를 읽고 어떤 방법으로 접근해야 하는지 찾아 주는 일, 한 거래처에서 통한 패턴을 다른 거래처로 넓히는 일입니다. 혼자였으면 A사의 좌표 클릭 방식에 도달하는 데만 며칠 걸렸을 겁니다.

못하는 건 앞의 인증 문제에서 드러났습니다. 원인을 잘못 짚었을 때 그럴듯한 설명을 계속 만들어 냅니다. 감지당하고 있다는 가설에 맞는 근거를 끝없이 대 줍니다. 팝업을 발견한 건 결국 실제 화면을 본 사람이었습니다.

그래서 방식을 바꿨습니다. 추측을 말하기 전에 실행할 때마다 화면을 찍어 두게 했습니다. 지금은 실행 한 건마다 소요 시간, 단계별 로그, 스크린샷, 받은 엑셀 파일이 전부 남습니다. 이력을 남기는 기능을 만드는 데 시간을 좀 썼는데, 그 뒤로 문제 찾는 속도가 확실히 빨라졌습니다.

정리하면 이렇습니다. AI에게 "왜 안 될까"라고 묻기 전에 "지금 화면이 어떻게 보이는지"를 먼저 만들어 두세요. 증거가 있으면 추측 단계가 통째로 사라집니다.

아직 완성이 아니고, 아마 계속 아닐 것 같다

거래처를 하나씩 늘려 가는 중이고 사이트가 바뀌면 또 고칩니다. 처음에는 언젠가 끝나는 프로젝트라고 생각했는데, 이제는 그렇지 않다는 걸 받아들였습니다. 남의 화면에 기대는 프로그램은 원래 이런 물건입니다.

가장 크게 남은 건 기술이 아니라 태도 쪽입니다. 이틀을 날린 그 문제는 제 추측이 틀렸다는 걸 인정하는 데 오래 걸려서 생긴 일이었습니다. 화면을 열어 보는 데는 1분이면 됐는데요.

다음에 비슷한 걸 만든다면 순서를 바꾸겠습니다. 기능보다 이력 남기는 기능을 먼저 만들겠습니다. 눈으로 볼 수 있는 게 있어야 나머지가 빨라집니다.

혹시 사무실에서 누군가 매일 아침 같은 화면을 대여섯 개씩 열고 있다면, 그 장면이 시작점입니다. 제 업무가 아니었는데도 만든 이유가 그거였습니다.