글

11월, 2026의 게시물 표시

직원이 직접 쓰는 예약 페이지 만들기 (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...

엑셀 예약표는 사흘 만에 무너졌다 (3)

이미지
문과생 유목이의 개발일지 · 3화 첫 시도는 아주 단순했다. 사내 공유 폴더에 엑셀 파일을 하나 만들었다. 세로축에 날짜, 가로축에 시간대를 놓고, 자원별로 시트를 나눴다. 쓸 사람이 자기 이름과 용도를 적으면 끝이다. 만드는 데 30분도 안 걸렸다. 공지도 올렸다. "이제 회의실 예약은 저한테 물어보지 마시고, 공유 폴더 예약표에 직접 적어주세요. — IT팀 유목" 사흘 정도는 정말 잘 굴러가는 것처럼 보였다. 문제 1. 동시에 열면 한 명이 사라진다 가장 먼저 터진 건 이거였다. 두 사람이 같은 파일을 동시에 열면, 나중에 저장한 사람의 내용만 남는다. 먼저 적은 사람의 입력은 그냥 사라진다. 본인은 분명히 적고 저장했는데, 나중에 열어보면 없다. 문제는 사라졌다는 사실을 아무도 모른다는 거다. 예약한 사람은 예약했다고 믿고 회의실로 간다. 가보면 다른 팀이 앉아 있다. 문제 2. 실수로 지워지면 끝 어느 날 다음 주 예약이 통째로 비어 있었다. 누군가 셀을 잘못 드래그해서 지운 것 같았는데, 확실하지 않았다. 언제 지워졌는지, 누가 지웠는지, 원래 뭐가 적혀 있었는지 확인할 방법이 없었다. 파일 이전 버전도 남아 있지 않았다. 결국 전 부서에 메신저를 돌렸다. "다음 주 회의실 예약하신 분들, 죄송하지만 다시 적어주시겠어요?" 이때 예약표에 대한 신뢰가 한 번 크게 깎였다. 문제 3. 중복예약을 막아주지 않는다 엑셀은 이미 채워진 칸에 다른 사람이 덮어써도 아무 말도 하지 않았다. 경고도, 확인창도 없다. 애초에 엑셀은 '이 칸이 이미 점유됐다'는 개념 자체가 없는 도구이다. 그냥 글자가 들어가는 상자일 뿐. 중복을 막으려면 사람이 눈으로 확인하는 수밖에 없다. 문제 4. 아무 기록도 남지 않는다 "저 분명히 예약했었는데요." 이 말이 나왔을 때 확인할 방법이 없다. 언제 누가 무엇을 적었고 언제 지웠는지가 전혀 남지 않으니, 결국 기억싸움이 된다. 그리...

회사에서 예약할 게 이렇게 많았나 (2)

이미지
문과생 유목이의 개발일지 · 2화 문제를 풀기로 마음먹고 가장 먼저 한 일은, 코드를 짜는 게 아니라 종이에 목록을 적는 일이었다. 우리 회사에서 '예약'이라는 단어가 붙는 것들을 전부 적어봤다. 적어보니 열 개가 넘었다 회의공간 대회의실 (3층) 소회의실 A, B (2층) 교육공간 교육실 (5층) 차량 법인차 3대 장비 촬영장비 세트 공용 노트북 4대 이동형 프로젝터 2대 직원 210명 규모 회사. 평소에 난 여기 구멍가게라고 자학을 하던 이 회사에 이리도 예약해야 할 목록이 많았단 말인가? 그리고 이것들은 관리 주체가 제각각이었다. 회의실은 내가, 법인차는 총무 담당 선배가, 촬영장비는 마케팅팀이 각자 자기 방식대로 관리하고 있었다. 법인차를 쓰려면 총무 선배한테 메신저를 보내야 하고, 촬영장비를 쓰려면 마케팅팀 단톡방에 물어봐야 했다. 그런데 구조는 전부 똑같았다 목록을 한참 들여다보다가 한 가지를 알게 됐다. 종류는 전부 다른데, 예약이라는 행위의 구조는 완전히 똑같았다. 누가 / 무엇을 / 언제부터 / 언제까지 사용하는가 회의실이든 법인차든 카메라든, 이 네 가지만 정해지면 예약은 성립한다. 그리고 "같은 물건을 같은 시간에 다른 사람이 이미 쓰고 있는가"만 확인하면 가능 여부도 판단된다. 대회의실 예약과 법인차 예약은 서로 다른 업무처럼 보이지만, 안을 들여다보면 완전히 같은 계산이다. 물건 이름만 바뀔 뿐이다. 그래서 방향을 이렇게 잡았다 여기서 프로젝트의 방향이 정해졌다. 회의실 예약표를 만드는 게 아니라, 자원 종류에 상관없이 쓸 수 있는 공용자원 예약 시스템을 만들자. 정리한 목록을 들고 갔더니 김 팀장님이 이렇게 물었다. "유목 씨, 회의실만 먼저 해보는 게 낫지 않아?" 맞는 말이었다. 범위를 좁히면 빨리 끝난다. 그런데 목록을 이미 적어본 뒤라 마음이 바뀌어 있었다. 회의실만 생각하고 만들면, 나중에 법인차를 붙일 때 "차량은 운행 기록...

또 회의실 예약 좀 잡아주세요 (1)

이미지
문과생 유목이의 개발일지 · 1화 출근해서 자리에 앉으면, 메신저 알림이 이미 여섯 개쯤 쌓여 있다. "유목님, 내일 오후에 대회의실 비어 있나요?" "유목님 다음 주 화요일 법인차 한 대만 잡아주세요." "촬영장비 목요일에 쓰려는데 누가 예약했는지 확인 좀…" 안녕하세요. 유목입니다. ㈜세움이라는 회사 경영지원부서 IT팀에서 일하고 있다. 입사 2년 차, 팀에서 막내고, 전공은 경영학. IT 지식이라곤 집에 있는 세탁기 돌리는 것과 스마트 티비가 전부이다. 그런 내가 회사에서 하는 일은 사내 PC와 계정 관리, 네트워크, 문서보안, 소프트웨어 라이선스, 그리고 회사에서 'IT'라는 글자가 조금이라도 붙은 모든 잡무이다. 그중 내 하루에서 가장 큰 지분을 차지하던 게 바로 이 예약 문의. 한 건에 10분, 하루에 열 건 요청이 들어오면 하는 일은 늘 똑같다. 예약표를 연다. 해당 날짜를 찾는다. 빈 시간을 확인한다. 메신저로 답을 보낸다. 한 건 처리하는 데 10분쯤 걸린다. 문제는 이게 하루 종일 끊기지 않고 한 번에 결정되는 적이 없다는 거다. 유목: "화요일 2시부터 4시까지는 비어 있습니다." 상대: "아 그 시간엔 팀장님이 외근이시라… 수요일은요?" 유목: (다시 처음부터) 문서 작업을 하다가 알림이 오면 창을 바꾸고, 예약표를 확인하고, 답을 보내고, 다시 원래 하던 일로 돌아온다. 돌아올 때마다 아까 뭘 하고 있었는지 다시 떠올려야 한다. 처리하는 10분에 앞뒤로 붙는 5분씩을 더하면, 한 건에 20분 가까이가 사라진다. 하루에 열 건 넘게 처리하면 다른 업무 처리를 못한 나는 자발적으로 야근을 할 수밖에 없게 된다. 진짜 곤란한 건 중복예약 그래도 여기까지는 참을 만하다. 곤란한 건 중복예약이 터졌을 때이다. 두 팀이 같은 회의실을 같은 시간에 잡아뒀다는 걸, 보통 회의 시작 10분 전에 알게 된다. 그러면 그때부...