Vibe coding으로 만드는 행낭 발송 캘린더 (1)

Yihoon·2025년 5월 22일
0

AWS활용기

목록 보기
9/9
post-thumbnail

Background

내 업무 중 하나는 해외 공관으로 나가는 문서들을 외교행낭 시스템에다가 접수하는 거다.

외교행낭(Diplomatic Pouch)은 외교부가 재외공관과 물품을 주고받는 데 사용하는 파우치를 의미한다. 외교용 특수 우편이라 생각하면 된다. 행낭으로 어떤 것들을 주고받는지는 웹에서 도는 이야기가 많던데, 이 부분은 조심스러운 내용인지라 말을 아끼겠다.

해외 공관마다 정해진 행낭 발송 스케줄이 있다.

예를 들면

  • 주 00 대사관은 매주 화요일 발송
  • 주 ## 총영사관은 격주 금요일 발송, ... 이런 식이다.

물론 행낭 업무와 직접 관련된 사람들은 행낭 관리를 위한 포털이 있기 때문에 발송 일정 확인을 '어찌저찌 찾아서' 할 수는 있다.
그러나 해당 포털에 권한이 없는 대부분의 실무관 분들, 그리고 외부 업체 사람들은 일정 확인이 어렵다. 해당 포털이 사용하기에 직관적이지 않기도 하고.

이들에게 배포하기 위해 200여개의 공관 목록과 발송 스케줄을 표 하나에 몰아넣은 '목록표'라는 게 있긴 한데, 이 표를 봐도 이게 어느 날짜에 나간다는 건지 알기가 쉽지 않다.
특히 '격주 발송' 공관은 홀수째 주와 짝수째 주로 나누어져 있는데, 이번 주가 홀수 주인지 짝수 주인지를 알고 있는 사람이 얼마나 되겠는가.

이 자료조차 없는 사람들은 매번 나한테 전화가 와서 '00 공관 언제 나가요?'라고 물어보기도 한다. 이 날짜에 발송되는 공관이 어디어디인지는 어디에도 정리되어 있지 않기 때문에, 발송 스케줄을 알려주는 업무는 여간 번거로운 계산이 아니다.
거기다 갑자기 휴일이 끼거나, 공관 측 요청으로 일정이 변경되는 경우가 매월 열 건 가량 발생하기에 이것까지 모두 파악하고 일정을 알려주어야 한다.

이 모든 번거로움을 해소하고자 전지전능한 vibe coding의 힘을 빌려 캘린더 웹 페이지를 구축하였다.
딱 하루만에 기획부터 배포까지 모든 작업을 끝내.. 고 싶었지만 모든 기능들을 구현하는 데에는 3일 정도의 시간이 걸렸고,
모바일 버전 쿼리를 추가하는 데 다시 하루 정도의 시간이 더 걸렸다.

프론트 코드와 Lambda 모두 Vibe coding의 작품이므로 아키텍처 위주로 간단하게 소개하고자 한다.
개발에는 Claude와 Copilot, Amazon Q를 섞어서 사용하였으며 썸네일 생성에만 ChatGPT를 사용하였다.

AWS Architecture

각 개체가 특정되는 걸 막고자 리전이나 ARN, ID를 모두 가리고 작성하였다.
행낭 발송 일정 또한 기밀은 아니나 웹상에 지나치게 Public하면 안 되는 정보라고 판단해 모두 가리고 작성하였다.

이번 프로젝트의 중점사항은 세 가지다.

  • 첫째, 최대한 빠르게 만들자. Vibe coding의 매력은 빠른 개발에 있다. 간편하고 신속하게 구축이 가능한 아키텍처를 사용한다.
  • 둘째, 프로덕션 수준의 안정성과 보안 수준은 포기한다. 많아야 스무 명 남짓의, 그것도 (CS 지식이 없는) 외교 실무 종사자들이 사용하는 앱을 만드는 데 그걸 다 신경쓰는 건 불필요하다고 생각했다. 취준과 업무를 병행하는 나로서는 시간이 너무 바쁜 것도 있고.
  • 셋째, 그치만 필요한 기능은 다 넣고 쓰기 쉬워야 한다. 필요하다면 같이 일하는 행정관님들도 이 앱을 사용할 수 있도록 쉽게 만들어야 한다.

중점사항이라 하기에는 민망하지만 나는 돈이 없으므로 비용도 중요한 요소였다. 물론 사용량이 극히 적은 서비스라 크게 걱정할 건 아니지만.
최종적으로 아래와 같은 심플한 아키텍처를 만들었다.

APIGW

먼저 REST APIGW를 만들었다.
확장을 대비해서 기본 메소드들을 모두 만들었다.

또 응답 포맷 관리를 쉽게 하기 위해 프록시 통합을 설정하였다.

DynamoDB

Lambda 함수를 다루기 전에 DynamoDB부터 확인해 보자.
DynamoDB 테이블을 두 개 사용한 건 각각의 목적이 다르기 때문이다.
하나는 각 공관별로 발송 일정을 담은 'Cities'로, 각 공관에 임의의 ID를 붙여 Partition Key로 사용한다.

  • scheduleType 속성은 크게 biweekly-1, biweekly-2, monthly, weekly 네 개의 값이 있다. 각각 홀수 격주 발송, 짝수 격주 발송, 매월 발송, 매주 발송을 뜻한다.
  • weekday 속성은 발송 요일을 뜻한다. 0 ~ 4가 각각 월~금요일에 해당한다.
  • monthlyWeek 속성은 monthly 공관에만 붙는 값이다. '매월 몇째 주 발송인가'를 뜻한다. 예를 들어 154번 공관은 매월 2째주 월요일 발송, 7번 공관은 매월 4째주 월요일 발송인 것.

    ID 값은 내가 테이블에 업로드한 순서로, 실제 재외공관 코드와 무관함을 밝힌다.

다른 하나는 변동된 값이 있는 테이블에 일정을 담는 'Schedules'로, 특정 날짜에 일정이 수정될 경우 해당 날짜에 최종 발송되는 공관 데이터가 쌓인다.
예를 들어 dateakey 값이 2025-05-21 인 항목은

[ 
  { "M" :
      { "name" : { "S" : "서울" },
        "weekday" : { "N" : "2" },
        "id" : { "N" : "3" },
        "scheduleType" : { "S" : "biweekly-1" } 
      }
  },
  { "M" :
     { "name" : { "S" : "부산" },
       "weekday" : { "N" : "2" },
       "id" : { "N" : "34" },
       "scheduleType" : { "S" : "biweekly-1" }
     }
  },
  ... (중략)...
]

와 같이 해당 일자에 발송되는 공관들과 그 발송 일정 유형이 담긴다. (당연히 서울, 부산은 더미 데이터이다)

Lambda

Node.js로 백엔드 통신을 위한 단일 함수를 생성하였다.

테이블 'Cities'는 /cities 엔드포인트와 연결된다.

  • 공관 목록 조회 (GET)
  • 새 공관 추가 (POST)
  • 공관 삭제 (DELETE)

테이블 'Schedules'는 /schedules 엔드포인트와 연결된다.

  • 일정 변경사항 조회 (GET)
  • 새 변경사항 저장 (POST)
  • 변경사항 삭제 (DELETE)

Cloudfront

이 앱을 어떻게 호스팅할지 많이 고민했다. 처음에는 보안팀과 이야기해서 내부망에 해당 페이지를 호스팅하는 걸 고려했지만, 내부망 접속이 불가능한 외부 업체 직원들도 해당 페이지를 사용해야 하고 공무원 분들도 본인의 업무 PC가 아닌, 내 자리인 문서 접수처에 와서 본인 핸드폰으로 일정 확인이 가능해야 하기 때문에 퍼블릭 호스팅이 필요했다. 애초에 일개 공익이 보안팀이랑 협의한다는 거 자체가 불가능이기도 하고.
개발 단계에서는 S3 퍼블릭 버킷에 정적 호스팅하였지만 Cloudfront를 이용하는 쪽으로 방향을 바꾸었다.

S3

빌드된 앱들은 모두 S3에 저장된다. 버킷의 퍼블릭 액세스는 모두 차단하였으며 대신 버킷 정책을 통해 Cloudfront를 통한 액세스를 허용하였다.

Client (React)

Vibe coding으로 나온 코드이지만 기능 소개도 할 겸 기본 구조는 알고 있는게 맞다고 생각해서 간단히 정리해 본다.

01

페이지 로드 시 먼저 공휴일 정보와 공관별 휴일 정보를 불러온다. 이어서 /schedules 메소드로 변경된 일정을 불러오고, 이들을 종합하여 캘린더에 표시한다. 캘린더는 generateCalendarDays라는 함수를 통해 이번 달을 7*6 테이블로 보여준다.
공휴일은 API로 당겨오는 방법도 생각했지만 전역 시점까지만 운영할 생각으로 단순히 하드코딩하는 방식을 택했다.

또 상단에는 오늘, 내일, 모레, 그리고 7일 후의 발송 일정을 표기하는데, 이들 값이 실제로 가장 많이 참조되는 일정이기 때문이다.
내일/모레 칸은 자동으로 휴일을 건너뛰며, 7일 후 칸이 휴일일 경우 14일 뒤를 표시하게 된다.

상단의 '공관 보기' 토글은 기본적으로 꺼져 있는데, 해당일에 발송되는 공관을 모두 표시하면 정보의 양과 스크롤 범위가 지나치게 많아지기 때문이다. 특정 날짜의 발송 스케줄을 확인하는 경우는 당일/익일/2익일/7익일이 가장 많기 때문에 해당 일정은 상단에 따로 표기하고, 아래의 캘린더는 토글을 통해 표기 여부를 선택할 수 있게 하였다.

앱 로드 시 플로우는 아래와 같다.

먼저 하드코딩된 공휴일 정보를 로드한다. 공휴일을 왜 API를 사용하지 않고 하드코딩했냐하면,1) 만족스러운 API를 찾지 못했고 2) 무엇보다도 내가 전역하고 나서까지 서비스를 유지하는 건 상정하지 않았기 때문이다.

이어서 /schedules.get 메소드로 발송 일정을 온다.

만약 일시적으로 일정 수정이 발생할 경우 /cities.get 메소드로 변경된 일정을 저장하게 된다.

이렇게 기본 로직에 따른 일정 + 변경된 일정을 조합하여 7by6 배열의 캘린더에 일정을 표기하게 된다. 자세한 내용은 아래에서 다루겠다.

02

한편 각 날짜별로 발송되는 공관을 계산하는 로직은 다음과 같다.
우선 휴일인 경우, 업무가 없기 때문에 빈 배열을 리턴한다.
휴일이 아닐 경우 테이블 B에서 임시 변경사항이 존재하는지 확인한다.
이날 변경된 일정이 존재한다면 해당 테이블에서 읽어 온 임시 변경사항을 로드하고, 아니라면 각 스케줄 유형별로 다가오는 발송 일정을 계산하게 된다.
이때 공휴일이 끼일 경우 일정이 어떻게 조정되는지는 원칙이 존재하기 때문에, 해당 원칙을 적용하여 별도 입력 없이 자동으로 변경된 일정이 계산되되도록 하였다.

이렇게 계산된 일정은 캘린더 모달에도 표시되지만 별도 모달을 통해서도 조회가 가능하다. 요청의 대부분이 특정 공관의 발송 일정을 물어보는 것이기 때문이다. 경험상 다음 세 번 정도의 일정까지는 자주 물어보나 그 이후로의 먼 일정은 크게 물어볼 일이 없을 뿐더러 일정이 변동될 가능성도 있기 때문에 표시하지 않는다.

03

끝으로 일정 수정 부분이다. 일정 수정과 같이 아무나 수정하면 안 되는 사항들은 관리자 모드에서만 수행이 가능하도록 비밀번호를 걸어 두었다. 비밀번호를 설정한 데에는 오조작을 막는 데에 주목적이 있기 때문에 별도의 보안 처리 없이 하드코딩한 값을 사용한다.

관리자 모드에서 캘린더의 특정 날짜를 클릭하면 모달이 표시되고, 여기서 체크박스 조작을 통해 해당 일자에 발송될 공관을 편집할 수 있도록 했다. 이는 공관 혹은 본부의 요청에 따라 언제든지 발송 일정이 추가될 수 있음을 고려하여 추가된 기능이다. 저장 버튼을 누르면 /schedules 메소드로 수정사항이 전송되어 테이블 B가 업데이트된다.

일정 변경의 대부분은 발송 일정을 연기하는 것인데, 이 경우 (원래 발송일)과 (수정된 발송일) 날짜를 각각 클릭하여 체크박스를 두 번 조작해야 한다. 이때 수정을 편리하게 하기 위해 공관명을 클릭하면 바로 날짜를 변경할 수 있게 한 가지 모달을 추가하였다.

앞에서 살펴본 두 기능은 모두 발송 일정을 일회성으로 수정하기 위함인데, 이런저런 사유로 발송 일정이 영구적으로 변경되거나, 전쟁 등의 사유로 발송이 중단되거나, 새롭게 공관을 설치해 일정이 추가되는 경우도 있다.
이 경우를 대비해 앞에서 살펴본 '공관 검색' 모달을 관리자 모드에서 접근하면 일정 편집 및 삭제가 가능하도록 하였다. 일반 모드에서는 볼 수 없는 '편집' '삭제' '추가' 버튼을 볼 수 있다.

04

후술하겠지만 해당 솔루션의 모바일 접근성을 높이기 위해 반응형 쿼리를 추가했다..

캘린더의 각 칸에 공관명을 모두 다 표기하기에는 공간이 부족했기에 캘린더의 날짜를 눌러 발송 공관을 확인할 수 있게 하였으며,

일정 검색 모달 또한 레이아웃을 상하로 배치하고 좀 더 타이트하게 잡았다.

완성된 서비스를 일부 직원들에게 배포하고 피드백을 요청한 결과 사용자경험에 있어 문제점들이 꽤 많았는데, 이 부분은 다음 편에서 다루도록 하겠다.

profile
딴짓 좋아하는 데이터쟁이

0개의 댓글