글

직원이 직접 쓰는 예약 페이지 만들기 (10)

이미지
문과생 유목이의 개발일지 · 10화 서버가 있고 창고가 있다. 이제 사람이 실제로 보는 화면을 만들 차례였다. 흐름부터 정했다 화면을 그리기 전에 순서를 정했다. 자원 고르기 → 날짜 고르기 → 시작시간 → 종료시간 → 이름·연락처 입력 → 신청 여섯 단계다. 더 줄이고 싶었지만 어느 하나도 뺄 수 없었다. 중요한 건 순서였다. 자원을 먼저 고르게 한 이유는, 그래야 그 자원의 빈 시간만 보여줄 수 있기 때문이다. 날짜를 먼저 물으면 회의실·차량·장비의 일정을 전부 불러와야 한다. Google Sheet가 느려졌던 이유가 정확히 그거였다. 빈 시간을 눈으로 보여주기 이게 이 페이지를 만드는 진짜 이유였다. 기존 방식에서 사용자는 빈 시간을 모른 채로 신청했다. 겹치면 반려 메일이 왔다. 4화에서 내가 "예약이 아니라 응모"라고 썼던 게 이 지점이다. 그래서 날짜를 고르면 그날의 시간대가 한눈에 깔리도록 만들었다. 이미 찬 시간은 회색으로 막아두고, 누를 수 없게 했다. 애초에 선택이 불가능하면 중복예약은 일어나지 않는다. 막힌 칸에 누가 예약했는지는 보여주지 않았다. "예약 있음"까지만 표시했다. 직원 전체가 서로의 일정을 들여다볼 이유는 없다고 판단했다. 핸드폰이 1순위였다 5화에서 가장 아쉬웠던 게 외근 나간 직원이었다. 돌아오는 길에 회의실을 잡아야 하는 사람이 정작 예약을 못 했다. 그래서 이번엔 순서를 뒤집었다. PC 화면을 만들고 핸드폰에 맞추는 게 아니라, 핸드폰 화면을 먼저 만들고 PC로 넓혔다. 기준은 하나로 잡았다. 한 손으로, 엄지로, 끝까지 갈 수 있는가. 그래서 입력칸을 최대한 줄였다. 시간은 직접 치는 대신 눌러서 고르게 했고, 소속은 목록에서 선택하게 했다. 박 과장님의 테스트 얼추 돌아가길래 박 과장님에게 링크를 보냈다. 5분 뒤에 메신저가 왔다. "종료시간이 시작시간보다 앞인데 그냥 넘어가는데?" 한번에 성공했다면, 내가 괜히 문과를 갔...

PostgreSQL과 친해지는 과정 (9)

이미지
문과생 유목이의 개발일지 · 9화 서버는 떴는데 저장할 곳이 비어 있었다. 이제 예약 데이터를 어떤 모양으로 담을지 정해야 했다. 왜 파일이 아니라 DB인가 사실 처음엔 "그냥 파일에 저장하면 안 되나" 싶었다. 엑셀도 결국 파일이었으니까. 그런데 엑셀이 왜 무너졌는지 떠올려보니 답이 나왔다. 두 사람이 동시에 쓰면 하나가 사라지고, 잘못된 입력을 막지 못하고, 기록이 남지 않았다. 데이터베이스는 정확히 이 세 가지를 해주는 물건이다. 같은 줄을 두 사람이 동시에 건드리면 순서대로 처리한다. 규칙에 어긋나면 거절한다. 누가 언제 바꿨는지 남긴다. 엑셀에 없던 게 전부 기본으로 들어 있었다. 예약 한 건에 뭐가 필요한가 종이에 다시 적었다. 예약 한 건을 설명하려면 무엇이 있어야 하는가. 예약번호 — 사람에게 알려줄 번호. "3번 줄"이라고 할 수는 없으니까 예약자 정보 — 이름, 연락처, 소속 자원 — 대회의실인지 법인차인지 시작시간 / 종료시간 — 충돌 판단의 핵심 예약상태 — 승인대기인지 확정인지 취소인지 용도 — 신청자가 적는 메모 관리자 메모 — 내가 따로 적는 칸 여기서 2화의 결정이 효과를 봤다. 자원을 따로 떼어서 관리하니, 회의실이든 차량이든 같은 표에 들어갔다. 자원을 하나 늘리는 건 줄 하나 추가하는 일이 됐다. 지우지 않기로 했다 가장 오래 고민한 게 취소 처리였다. 처음엔 당연히 지우는 거라고 생각했다. 취소된 예약을 왜 들고 있나 싶었다. 그런데 엑셀 때가 떠올랐다. 누가 언제 지웠는지 몰라서 전 부서에 메신저를 돌렸던 날. 지워버리면 그때로 돌아가는 것이었다. 그래서 지우지 않고 상태를 바꾸는 방식 으로 정했다. 취소된 예약도 줄은 그대로 남고, 상태 칸만 "취소"로 바뀐다. 누가 언제 왜 취소했는지가 남는다. 이 결정 하나로 나중에 꽤 많은 게 편해졌다. 잘못 취소한 예약을 되살리는 기능도, 월별 사용 통계도 전부 여기서 나왔다. 시간 저장에...

Docker로 예약 서버 올리기 (8)

이미지
문과생 유목이의 개발일지 · 8화 구조를 이해했으니 이제 만들면 된다. 그렇게 생각했다. 실제로는 첫 화면이 뜨기까지 사흘이 걸렸다. 파일 세 개로 시작 NAS에 폴더를 하나 만들고, 그 안에 파일 세 개를 뒀다. Dockerfile — 주방을 어떻게 차릴지 적는 설명서다. Node.js를 깔고, 내 코드를 넣고, 서버를 실행하라는 순서가 들어간다. docker-compose.yml — 주방이 두 개 필요하다. 하나는 서버, 하나는 데이터베이스다. 이 둘을 같이 띄우고 서로 연결해주는 설정 파일이다. 소스 코드 폴더 — 실제 예약 로직이 들어갈 자리다. 이 시점에는 "안녕하세요"만 출력하는 파일 하나가 전부였다. Container Manager에서 실행 버튼을 눌렀다. 컨테이너 두 개가 올라갔다. 여기까지는 순조로웠다. 그리고 사흘을 날렸다 첫 번째. 포트 충돌. 서버를 5000번 포트에 올렸더니 아예 뜨질 않았다. 알고 보니 DSM이 이미 쓰고 있는 번호였다. 남의 집 현관문에 내 문패를 달려고 한 셈이다. 3000번으로 바꾸니 바로 해결됐다. 두 번째. 데이터베이스 연결 실패. 이게 제일 오래 걸렸다. 서버가 계속 데이터베이스를 못 찾는다고 했다. 설정에는 분명 localhost 라고 적혀 있었다. 문제는 컨테이너가 각각 별개의 공간이라는 점이었다. 서버 컨테이너 입장에서 localhost 는 "나 자신"이다. 옆 컨테이너가 아니다. 그러니까 나는 주방에 대고 "창고 어디 있어?"가 아니라 "내가 어디 있어?"라고 물어보고 있었던 것이다. localhost 대신 컨테이너 이름을 적으니 바로 붙었다. 세 번째. 비밀번호 인증 실패. password authentication failed for user 라는 메시지가 떴다. 설정 파일의 비밀번호와 데이터베이스가 처음 만들어질 때 저장된 비밀번호가 달랐다. 중간에 비밀번호를 바꿨는데, 이미 만들어진 데이터베...

개발을 잘 모르는데 서버부터 만들기 (7)

이미지
문과생 유목이의 개발일지 · 7화 NAS에 예약 시스템을 올리기로 했다. 목마른 자가 우물을 판다고 그랬나? 다시 퇴근하고 매일 3시간씩 독학을 시작했다. 그래도 세상이 좋아지고 숨어있는 고수들이 많아서 그런지 인터넷에서 여기 저기 검색을 해봤더니 여러 힌트가 나왔다. Node.js, Express, PostgreSQL, Docker, API, 포트, 컨테이너, 리버스 프록시. …… 이상한 단어 나열의 연속이였다. 하나도 몰랐다. 정확히는 지나가면서 봤는데 무엇이고 뭘 하는지 설명할 수 없었다. 경영학과를 나와서 사내 PC 고쳐주고 사는 나에게 기본 지식이란건 존재하지 않았다. 식당으로 바꿔서 이해했다 누군가 이걸 식당에 비유해서 적은 글을 보게 되었다. 그리고 지금 내 예약 사이트를 거기에 대입해서 정리하기 시작했다. 예약 웹페이지 는 메뉴판이다. 손님이 보고 고르는 화면. 직원이 브라우저에서 보는 게 이거다. API 서버(Node.js + Express) 는 주방이다. "대회의실 화요일 2시"라는 주문을 받아서, 가능한지 확인하고, 결과를 돌려준다. 손님은 주방에 직접 들어가지 않는다. 주문서만 주고받는다. 데이터베이스(PostgreSQL) 는 창고다. 예약 기록이 전부 여기 쌓인다. 주방은 요리를 할 뿐이고, 재료는 창고에 있다. Docker 는 주방을 통째로 담아둔 컨테이너다. 가스레인지, 칼, 냄비를 하나하나 설치하는 대신, 필요한 게 전부 들어 있는 주방 세트를 통째로 NAS에 올려놓는 것이다. 이 비유가 정확한지는 지금도 모르겠다. 다만 이렇게 보니 무엇부터 만들어야 하는지는 알 것 같았다. 전체 구조 정리하면 이렇다. 직원 브라우저 → 예약 웹페이지 → API 서버 → 데이터베이스 요청은 왼쪽에서 오른쪽으로 가고, 결과는 오른쪽에서 왼쪽으로 돌아온다. 그게 전부다. Google Sheet 방식과 비교하면 차이가 분명해졌다. Sheet에서는 이 네 칸이 전부 뭉쳐 있었다. 화면도 Sheet, 계산...

회사에 있던 NAS를 예약 서버로 써보기로 했다 (6)

이미지
문과생 유목이의 개발일지 · 6화 예약 시스템을 직접 만들기로 마음은 먹었는데, 그다음 줄에서 바로 막혔다. 서버가 없었다. 돈 드는 방법은 전부 탈락 방법은 세 가지였다. 첫째, 서버를 산다. 경영지원부서 막내가 "서버 한 대 사주세요"라고 품의를 올리는 장면을 상상해봤다. 결재선 두 번째 칸에서 "이거 왜 필요해?"라는 질문이 돌아올 게 뻔했다. 그 질문에 "제가 예약 시스템을 만들어보려고요"라고 답하는 순간, 난 사표를 제출해야 할 수도 있다. 아직 만들 수 있다는 확신도 없는 상태에서 무슨 용기로 그런 주장을 할 수 있겠나? 둘째, 클라우드를 쓴다. 월 몇만 원이면 된다는 글을 많이 봤다. 그런데 몇만 원이라도 매달 내 주머니에서 돈이 나가야 한다는 건 끔찍하다. 그렇다고 품의를 올린다? 게다가 코딩의 ㅋ도 모르는 내가 무언가 잘못 설정해서 돈이 추가로 나간다면? 끔찍하다. 셋째, 이미 있는 걸 쓴다. 이게 유일하게 남은 선택지였다. 서버실 구석의 NAS 회사 서버실에 외부에 합사를 나갈 경우 설치해주는 구형 Synology NAS가 한 대 있다. 너무 오래되고 용량이 작아서 최근에 최신 사양을 구매하면서 이 친구는 창고에 덩그러니 남겨져 있다. 내가 관리하고 있었으니 안에 뭐가 들어 있는지도 알았다. 평소엔 그냥 "큰 외장하드" 정도로 우리 부서 서류 백업 하는 정도로만 쓰고 있다. DSM에 접속해서 패키지 목록을 쭉 내려보는데 Container Manager 라는 게 눈에 들어왔다. 조금 공부해보니 이 녀석은 Docker라는 것이였다. 이것이 있다는건, 이 NAS가 웹서버와 데이터베이스를 직접 돌릴 수 있다는 뜻이었다. 조건을 하나씩 따져보니 나쁘지 않았다. 24시간 켜져 있다. 서버의 기본 조건이다. 사내망 안에 있다. 직원들이 바로 접속할 수 있다. 이미 회사 자산이다. 추가 비용이 0원이다. 내가 관리자다. 허락받을 사람이 적다. 마지막 항목이 사실 제...

어느 순간 시트와 싸우고 있었다 (5)

이미지
문과생 유목이의 개발일지 · 5화 Google Sheet 방식은 잘 돌아갔다. 문제가 생긴 건 예약 건수가 쌓이고, 관리하는 자원이 늘면서부터였다. 1. 느렸다 예약 가능 시간을 확인하려면 Sheet에 쌓인 데이터를 전부 불러와서 하나씩 비교해야 했다. 데이터가 몇백 건을 넘어가자 반응이 눈에 띄게 느려지기 시작했다. 사용자는 제출 버튼을 누르고 결과가 나올 때까지 화면을 보며 기다린다. 처음엔 몇 초짜리 대기였다. 한 주 한 주 예약이 쌓이면서 지금은 거의 10분을 기다려야 하고, 어쩔 때는 불러와지지도 않기 시작했다. "유목님, 이거 눌렀는데 아무 반응이 없는데요?" "두 번 눌러도 되나요?" 두 번 누르면 신청이 두 건 들어온다. 그럼 내가 하나를 지워야 한다. 없애려던 수작업이 다른 모양으로 돌아왔다. 2. Apps Script가 점점 손대기 무서워졌다 기능을 하나 추가할 때마다 스크립트가 길어졌다. 처음엔 "신청이 들어오면 메일을 보낸다" 한 줄짜리 로직이었는데, 여기에 자원별 분기가 붙고, 승인 여부 분기가 붙고, 취소 처리가 붙었다. 어느 순간부터는 한 곳을 고치면 다른 곳이 왜 멈추는지 알 수 없게 됐다. 비전공자가 붙인 코드라 구조랄 것도 없었다. 문제가 생기면 고치는 게 아니라, 되던 시점으로 되돌리고 다시 붙였다. 짜깁기도 이런 짜깁기가 없었다. 아마도 전문가가 보면 비웃었을 것이다. 3. 자원이 늘어나니 시트 구조가 감당이 안 되었다 회의실 하나일 때는 시트 하나면 충분했다. 그런데 대회의실, 소회의실 A·B, 교육실, 법인차 3대, 촬영장비, 노트북까지 들어오자 구조부터 막히기 시작했다. 자원별로 시트를 나누면 → 전체 현황을 한 번에 볼 수 없음 한 시트에 다 넣으면 → 필터 조건이 복잡해지고 더 느려짐 둘 다 해봤고, 둘 다 만족스럽지 않았다. 4. 원하는 화면을 만들 수 없었다 이게 가장 답답했다. 처음 내가 만들고 싶었던 건 "이 회의실...

이번엔 진짜 될 줄 알았다 (4)

이미지
문과생 유목이의 개발일지 · 4화 엑셀의 한계는 명확했다. 조건은 두 가지로 정리됐다. 첫째, 파일이 아니라 온라인에서 돌아가야 한다. 여러 사람이 동시에 써도 데이터가 사라지지 않아야 한다. 둘째, 사람이 확인하는 단계를 줄여야 한다. 신청부터 등록까지 자동으로 이어져야 한다. 회사에서 이미 Google Workspace를 쓰고 있었기 때문에, 추가 비용 없이 쓸 수 있는 도구가 눈앞에 있었다. 새 솔루션을 사자고 품의를 올릴 자신이 없던 나 같은 말단에게는 돈이 가장 중요하다. 그려본 그림 Google Form (신청) → Google Sheet (저장) → Apps Script (자동 처리) → 관리자 확인 → Google Calendar (등록) → 이메일 알림 각 단계의 역할은 이렇다. Google Form — 신청 창구 사용자가 자원, 날짜, 시작시간, 종료시간, 이름, 연락처, 용도를 입력한다. 링크 하나만 공유하면 되니 배포가 간단하다. Google Sheet — 저장소 Form에 제출된 내용이 자동으로 한 줄씩 쌓인다. 엑셀과 달리 여러 명이 동시에 제출해도 서로의 데이터를 덮어쓰지 않는다. 제출 시각도 자동으로 기록된다. Apps Script — 자동 처리 Google 문서에 붙일 수 있는 스크립트이다. "새 행이 추가되면 이걸 실행해라" 같은 자동화를 걸 수 있다. 메일 발송과 캘린더 등록을 여기서 처리했다. Google Calendar — 현황 공유 확정된 예약을 캘린더에 등록. 직원들이 캘린더만 보면 어느 회의실이 언제 차 있는지 알 수 있었다. 이메일 알림 신청이 들어오면 나에게, 승인되면 신청자에게 자동으로 메일이 가서 체크할 수 있었다. 실제로 나아졌다 이 구조는 엑셀보다 확실히 나았고, 실제로 꽤 오래 이 방식으로 안정적으로 운영되었다. 데이터가 사라지지 않았다. 동시 제출로 입력이 날아가는 일이 없어졌다. 기록이 남았고, 누가 언제 무엇을 신청했는지 Sheet...