Codex로 팀용 웹앱을 만들고 배포해봤습니다.
아이디어를 요구사항으로 바꾸고 AI와 함께 팀용 웹앱을 만들고 배포해봤습니다. 긴 프롬프트보다 단순한 요구사항이 더 좋은 결과를 만들기도 했고, GitHub·Cloudflare를 활용한 버전 관리와 배포 과정에서 얻은 시행착오와 배운 점을 정리했습니다.
"엇 오늘 사무실에 없네요?" "아 오늘 재택입니다."
동료와 잠깐 이야기하려고 자리를 찾았지만, 그날은 사무실에 없는 경우가 있습니다. 팀원의 작업 위치를 간단히 공유하는 앱을 만들어보면 어떨까 생각했습니다.
Codex로 만든 작업위치 공유 앱
처음에는 ChatGPT에 “작업 위치 공유 앱을 만들고 싶어”라고 입력했습니다. 이후 “GPS를 기반으로 만들려면 어떻게 해야 할까?”, “실시간으로 추적하지 않고, 위치 공유 버튼을 누른 순간의 정보만 저장하려면 어떻게 해야 할까?”와 같은 질문을 이어갔습니다.
이 대화를 통해 사용자가 원할 때만 현재 위치를 남기는 방향으로 요구사항을 구체화했습니다.
ChatGPT와의 대화를 통해 정리한 요구사항을 Codex에 전달해 웹앱을 만들었습니다.
최근 테스트를 대부분 HTML, CSS, Plain JavaScript 환경에서 진행했기 때문인지, 요구사항의 기술 스택도 해당 환경을 기준으로 작성되었습니다.
제가 입력한 요청은 다음과 같습니다.
작업위치 공유 앱을 만들어줘
```
## 목적
팀원이 현재 작업 중인 위치를 간편하게 공유할 수 있는 웹앱을 만든다.
사용자가 **'위치 공유' 버튼을 누른 순간의 위치만 저장**하며, 팀원들은 각 사용자가 **마지막으로 공유한 작업 위치**를 확인할 수 있다.
실시간 위치 추적이나 지속적인 위치 업데이트는 제공하지 않는다.
---
# 기술 스택
## Frontend
- HTML
- CSS
- Plain JavaScript
## Backend
- Cloudflare Pages Functions
## Database
- Cloudflare D1
## Hosting
- Cloudflare Pages
## 지도
- OpenStreetMap
## 위치 정보
- Browser Geolocation API
---
# 배포 환경
Cloudflare에서 바로 배포할 수 있도록 구성한다.
포함 사항
- Cloudflare Pages
- Cloudflare Pages Functions
- Cloudflare D1
- Wrangler
- README
---
# 주요 기능
## 로그인
간단한 로그인 기능을 제공한다.
입력 항목
- 이름
- 이메일
동작
- 이메일 기준으로 사용자 조회
- 처음 로그인한 사용자는 자동 등록
---
## 작업 위치 공유
사용자가 **'위치 공유'** 버튼을 누르면 현재 위치를 가져와 저장한다.
저장 항목
- 위도
- 경도
- 위치 정확도
- 주소
- 메모
- 공유 시각
같은 사용자가 다시 위치를 공유하면 이전 정보를 새로운 위치로 갱신한다.
---
## 팀 작업 위치 조회
모든 팀원의 마지막 작업 위치를 조회한다.
각 팀원 카드에는 다음 정보를 표시한다.
- 이름
- 이메일
- 마지막 작업 위치 주소
- 메모
- 마지막 공유 시각
- 지도 보기 버튼
---
# 화면 구성
## 로그인 화면
- 이름 입력
- 이메일 입력
- 로그인 버튼
---
## 메인 화면
### 내 작업 위치
- 위치 공유 버튼
- 메모 입력
- 마지막 공유 시각
- 마지막 공유 위치
### 팀원 작업 위치
카드 형태로 표시
각 카드에는
- 이름
- 이메일
- 마지막 작업 위치
- 메모
- 마지막 공유 시각
- 지도 보기 버튼
---
# 위치 수집
Browser Geolocation API를 사용한다.
사용자가 **위치 공유 버튼을 클릭한 순간** 현재 위치를 가져온다.
위치 정보를 가져온 후 Cloudflare D1에 저장한다.
주소는 Reverse Geocoding API를 이용하여 생성한다.
위치 조회 실패 시 사용자에게 다음 내용을 안내한다.
- 위치 권한이 허용되지 않았습니다.
- 위치 정보를 가져올 수 없습니다.
- 위치 조회 시간이 초과되었습니다.
---
# 데이터베이스
## members
| 컬럼 | 타입 |
|------|------|
| id | INTEGER PRIMARY KEY |
| name | TEXT |
| email | TEXT UNIQUE |
| created_at | TEXT |
| updated_at | TEXT |
---
## work_locations
| 컬럼 | 타입 |
|------|------|
| member_id | INTEGER PRIMARY KEY |
| latitude | REAL |
| longitude | REAL |
| accuracy | REAL |
| address | TEXT |
| note | TEXT |
| shared_at | TEXT |
| updated_at | TEXT |
사용자당 하나의 작업 위치만 저장한다.
새로운 위치를 공유하면 기존 데이터를 갱신한다.
---
# API
## POST /api/login
입력
```json
{
"name": "",
"email": ""
}
```
동작
- 사용자 조회
- 사용자 생성
- 사용자 정보 반환
---
## POST /api/location/share
입력
```json
{
"member_id": 1,
"latitude": 37.5665,
"longitude": 126.9780,
"accuracy": 15,
"address": "",
"note": ""
}
```
동작
- 현재 작업 위치 저장
- 기존 위치 갱신
- 공유 시각 저장
---
## GET /api/team
동작
- 모든 팀원의 마지막 작업 위치 조회
---
# UI
- 반응형으로 구현한다.
- 카드 형태 UI를 사용한다.
- 마지막 공유 시간이 최신인 사용자가 위쪽에 표시된다.
- 지도 보기 버튼을 클릭하면 OpenStreetMap이 새 창에서 열린다.
- 위치 공유 완료 시 성공 메시지를 표시한다.
- 오류 발생 시 원인을 안내한다.
---
# 파일 구조
```
/
├── public/
│ ├── index.html
│ ├── styles.css
│ └── app.js
│
├── functions/
│ └── api/
│ ├── login.js
│ ├── team.js
│ └── share.js
│
├── schema.sql
├── wrangler.toml
├── package.json
└── README.md
```
---
# README
다음을 포함한다.
- 프로젝트 설치
- Cloudflare 로그인
- D1 생성
- Wrangler 설정
- 로컬 실행
- D1 마이그레이션
- Cloudflare Pages 배포
---
# 구현 원칙
- 기능별로 파일을 분리한다.
- 유지보수가 쉬운 구조로 작성한다.
- 주요 함수에 주석을 작성한다.
- 모바일과 데스크톱 환경을 모두 지원한다.
- Cloudflare 무료 플랜에서 실행 가능하도록 구현한다.
- MVP 수준의 완성도 높은 결과물을 제공한다.
```분량이 아주 많은 것은 아니지만, 항목이 지나치게 세분화되어 있어 전체 구조가 한눈에 들어오지 않았습니다. 다음에는 처음부터 프롬프트를 짧게 정리해 달라고 요청하거나, 구현 방법을 지나치게 구체적으로 제한하지 않는 방식도 시도해봐야겠습니다.
우선 이 프롬프트를 Codex에 입력해 결과를 확인했습니다.

결과물은 무난했지만, 실제 위치와 다소 차이가 있었습니다. 또한 근사치라고 하더라도 위치 정보가 기록으로 남는다는 점이 불편하게 느껴졌습니다.
배포는 Codex Sites와 Cloudflare Pages 두 가지 방식으로 진행했습니다.
Codex Sites는 별도의 복잡한 확인 절차 없이 배포할 수 있었습니다. 반면 Cloudflare는 Wrangler가 수행할 작업을 확인하고 권한을 승인하는 등 사용자가 중간에 개입해야 하는 과정이 있었습니다.
사용자 개입이 필요했음에도 Cloudflare 배포는 6분 남짓 걸렸고, Codex Sites 배포는 약 20분이 걸렸습니다. (참고: 위 프롬프트를 바탕으로 Codex Sites에서는 목적 중심으로 프롬프트를 단순화해 다시 실행했습니다.)
요구사항이 너무 복잡했던 것은 아닐까?
이 과정에서 요구사항이 지나치게 길어져 내용을 확인하기 어려웠고, 결과물도 기대에 완전히 부합하지는 않았습니다. 그래서 요구사항을 대폭 줄인 뒤 Codex에서 다시 시도했습니다.
작업위치 공유 앱을 만들어줘. 가장 단순하게, 개인정보 이슈 없게, 오늘 각자 어디쯤에서 일하고 있는지만 알 수 있게, 이력 필요 없음, 위치 공유 편리하게.
약 9분이 걸렸고, 결과물은 예상보다 만족스러웠습니다. GPS 정보를 수집하는 대신 집, 사무실, 카페, 이동 중 등의 항목을 직접 선택하고 간단한 내용을 남기는 방식이었습니다. 처음부터 문제를 불필요하게 복잡하게 설계했던 것 같습니다.
기술 스택 바꾸기
다만 이 앱은 React를 기반으로 만들어져, 기능의 단순함에 비해 구조가 다소 무겁게 느껴졌습니다. 이를 HTML, CSS, Plain JavaScript 방식으로 변경하려 했지만, 그 과정에서 문제가 발생했습니다.
변경 전후를 비교하려면 기존 코드를 남겨두어야 했습니다. 하지만 사본이나 이전 버전을 만들지 않은 채 작업을 시작했고, 도중에 실수로 작업을 중지했습니다. 몇 초 만에 중지했지만 이미 일부 코드가 수정된 상태였고, 이를 원래대로 되돌릴 수 없었습니다.
Codex에서도 일반적으로 변경 사항을 되돌릴 수 있지만, 이처럼 복구하지 못한 경험이 있어 불안함이 남았습니다.

이후 배포를 다시 시도했습니다. 진행 중 발생한 npm 오류와 SyntaxError는 오류 메시지를 Codex에 전달해 해결했습니다.
Codex Sites에서 발생한 HTTP 500 오류는 한두 차례 원인을 점검한 뒤, 아래 프롬프트를 사용해 해결했습니다.
Codex Sites 배포 후 HTTP 500 오류가 발생합니다.
다음을 확인해서 원인을 찾아주세요.
1. 배포 로그와 런타임 로그에서 실제 에러 메시지 확인
2. 누락된 환경변수 확인
3. API route 또는 서버 함수에서 예외가 나는 지점 확인
4. 로컬에서는 되는데 배포에서만 실패하는 코드 확인
5. 필요한 경우 try/catch와 에러 로그를 추가해서 원인 파악 가능하게 수정
특히 SLACK_WEBHOOK_URL, DATABASE_URL 등 배포 환경변수가 필요한 코드가 있는지 확인해주세요.
원인을 찾고 [@sites](plugin://sites@openai-bundled) 에 배포해주세요.하지만 “지금 정상적으로 작동하는 것만으로 충분할까?”라는 의문이 들었고, 버전 관리가 필요하다고 판단했습니다.
Codex Sites는 최초 배포는 간편했지만, 이후 수정 사항을 관리하며 반복적으로 배포하기에는 불편했습니다.
기존 코드에서 요구사항을 추출해 재현하기
이미 코드베이스가 불안정해진 것은 아닐까 걱정되었습니다. 기존 코드를 계속 수정하기보다는, 현재 코드를 바탕으로 요구사항을 다시 추출해 처음부터 재현해보기로 했습니다.
해당 프로젝트 코드를 보고 다시 프롬프트로 뽑아줘Codex에서 프로젝트 폴더를 연 상태로 요청했고, 다음과 같은 프롬프트를 얻었습니다.
한국어 팀용 웹앱 “오늘, 어디서 일해?”를 만들어줘.
목적:
팀원이 오늘 일하는 대략적인 장소를 가볍게 공유하는 일회성 작업 위치 보드다. GPS, 정확한 주소, 위치 권한, 위치 이력은 절대 사용하지 않는다. 사용자가 직접 선택한 범주만 공유하며, 모든 데이터는 매일 자정 자동 초기화된다.
기술:
- Next.js App Router + TypeScript
- React 클라이언트 컴포넌트
- Cloudflare Workers + D1 데이터베이스 지원
- REST API: GET/POST/DELETE `/api/people`, DELETE `/api/people/[id]`
- D1 테이블은 `people(id, name UNIQUE, place, detail, note, updated_at, color, board_date)` 구조
- API 요청마다 오늘 날짜가 아닌 데이터는 삭제한다.
- 같은 이름으로 다시 등록하면 새 행을 만들지 말고 기존 사용자의 위치를 업데이트한다.
- 이름은 14자, 기타 장소 상세는 24자, 한마디는 36자로 제한한다.
화면 구성:
1. 상단 바
- 좌측 브랜드: “오늘, 어디서 일해?”
- 우측: 한국어 형식의 오늘 날짜 (예: 7월 10일 (금))
- 얇은 하단 구분선
2. 히어로
- 작은 모노스페이스 영문 라벨: “TODAY’S WORK MAP”
- 큰 제목: “지금, 어디쯤에서 / 일하고 있나요?”
- 설명: “정확한 위치 대신, 편한 범위로 알려주세요. 오늘의 상태만 남고 내일이면 새로 시작해요.”
3. 내 작업 위치 입력 카드
- 카드 제목: “MY UPDATE”, “내 작업 위치”
- 우측 연한 민트 배지: “위치 추적 안 함”
- 이름 입력란
- 위치 선택 버튼 5개: 집(⌂), 사무실(▥), 카페(◒), 이동 중(→), 기타(+)
- 선택된 버튼은 짙은 차콜 배경과 흰 글자
- ‘기타’를 선택했을 때만 “대략적인 장소 (선택)” 입력란 노출
- “한마디 (선택)” 입력란
- 전체 너비의 짙은 버튼: “오늘의 위치 공유하기 ↗”
- 저장 오류 시 한국어 오류 메시지 표시
4. 팀 현황
- 작은 라벨: “TEAM TODAY”
- 제목: “동료들은 여기 있어요 [인원 수]”
- 우측 텍스트 버튼: “오늘 기록 비우기”
- 데이터가 없을 때: 원형 아이콘과 “아직 공유한 동료가 없어요. 첫 번째 업데이트를 남겨보세요.”
- 데이터가 있으면 최신 업데이트 순으로 목록 표시
- 원형 아바타: 이름 첫 글자
- 이름
- 노란색 위치 태그: “집”, 또는 “기타 · 성수동 근처”
- 한마디가 없으면 “오늘 여기에서 일하고 있어요.”
- 상대 시간: 방금 / N분 전 / N시간 전
- 각 항목에 삭제 버튼
- 전체 삭제와 개별 삭제 전에는 confirm 창을 표시한다.
디자인:
- 차분하고 따뜻한 미니멀 업무 도구 스타일
- 최대 콘텐츠 너비 760px, 중앙 정렬
- 배경: #f8f8f5
- 본문 글자: #20211d
- 구분선: #deded8
- 포인트 민트: #c9e5d5
- 노랑: #f4d758
- 아바타 색상은 민트, 노랑, 연파랑, 코랄, 연보라를 순환 사용
- 입력 카드는 흰 배경, 얇은 테두리, 우측·하단 방향의 민트색 5px 하드 섀도
- 본문 폰트는 Noto Sans KR, 날짜·영문 라벨·아바타는 DM Mono
- 제목은 굵고 촘촘한 자간, 큰 여백을 사용
- 모바일 540px 이하에서는 여백과 제목 크기를 줄이고, 목록의 상대 시간은 숨긴다.
- 푸터 문구: “정확한 위치 · 위치 이력 · 자동 추적 없음 · 매일 자정에 초기화”
접근성:
- label, fieldset, legend를 올바르게 사용한다.
- 목록은 aria-live를 적용한다.
- 버튼과 입력 요소의 키보드 포커스 상태를 제공한다.
완성도:
- API 오류를 사용자 친화적인 한국어 문구로 처리한다.
- 목록은 저장, 삭제, 전체 삭제 직후 즉시 다시 불러와 갱신한다.
- 개인정보 원칙이 UI와 동작 양쪽에서 명확히 지켜지도록 구현한다.추출된 프롬프트는 여전히 Next.js와 React를 기준으로 작성되어 있었습니다. 이를 HTML, CSS, Plain JavaScript 기반으로 바꾸기 위해 다음과 같이 요청했습니다.
아래 요구사항 프롬프트를 보고 html, css, plain js로 만드는걸로 관련 내용을 수정해줘. 그리고 디비랑 배포 등 관련 시스템은 cloudflare로 하고 싶어.

일부 불필요한 내용이 추가되었지만, 전체 구조에는 큰 문제가 없어 그대로 사용했습니다.
버전 관리는?
이후 로컬 터미널과 Codex 터미널에서 Git을 사용하는 방법을 여러 차례 테스트했습니다. 하지만 관련 개념을 충분히 이해하지 못한 상태에서 진행하다 보니 과정이 순탄하지 않았고, 결국 기존에 익숙한 방식을 사용했습니다.
- GitHub 웹사이트에서 저장소를 생성합니다.
- GitHub Desktop에서 해당 저장소를 로컬로 복제합니다.
- Codex에서 복제한 프로젝트 폴더를 엽니다.
- Codex를 통해 프로젝트를 수정하고 Cloudflare에 배포합니다.
- Cloudflare와 GitHub 저장소를 연결합니다.
- Visual Studio Code에서 변경 사항을 검토한 뒤 커밋(commit)하고 배포합니다.


왼쪽은 처음 만든 화면이고, 오른쪽은 기존 코드에서 추출한 프롬프트로 다시 만든 화면입니다.
두 결과물은 예상보다 상당히 비슷했습니다. 기존 코드를 분석해 화면과 기능을 요구사항으로 변환한 프롬프트가 비교적 정확하게 작성된 것으로 보입니다.
GitHub 저장소에 코드를 올리기 전에는 보안 점검을 위해 다음과 같이 요청했습니다. 점검 결과에 따라 .gitignore 파일도 추가했습니다.
github repo에 올리면 안되는 내용이 있나?하나씩 개선해보기
이후에 위치를 공유할 때 슬랙으로도 알림을 보내면 좋겠다는 의견이 나왔습니다.
Codex에서 해당 프로젝트를 연 뒤 새 스레드를 시작합니다.

Slack API에서 Incoming Webhook을 설정합니다.

Cloudflare 설정에 SLACK_WEBHOOK_URL 환경 변수를 등록합니다.

Cloudflare와 GitHub를 연결해 커밋할 때마다 자동으로 배포되도록 설정합니다.
Visual Studio Code나 GitHub Desktop에서 변경 사항을 확인한 뒤 커밋합니다.
배포된 앱에서 위치를 공유해 슬랙 알림이 정상적으로 도착하는지 확인합니다.

배운 점
간단한 사내용 앱은 아이디어를 요구사항으로 정리한 뒤, 짧은 시간 안에 구현하고 테스트해 배포할 수 있었습니다.
다만 요구사항을 어느 정도까지 구체적으로 작성할지는 계속 고민할 필요가 있었습니다. 또한 수정한 코드에 문제가 생겼을 때 이전 상태로 돌아가거나 변경 내역을 기록하려면, Codex Desktop만으로 작업하기에는 한계가 있어 보였습니다.
GitHub와 Cloudflare를 사용하려면 관련 지식을 조금씩 익혀야 합니다. 하지만 버전과 배포 과정을 관리할 수 있다는 점에서 앱을 수정할 때 훨씬 안정적으로 느껴졌습니다.