안녕하세요. 스튜디오 폴라리스입니다.
스튜디오 폴라리스는 AI를 비롯한 다양한 컨텐츠를 도서, 앱서비스, 영상 등의 다양한 방식으로 제공하는 소규모 컨텐츠 제작/개발자 모임입니다.
첫 글로, 현재 실시간으로 작업중인 앱서비스 개발 이야기를 함께 나눠보고자 합니다.
저는 모바일 앱서비스의 프로덕트 매니저로 일해왔으며, 최근 AI를 이용한 1인 개발을 여러가지 시도해 보고 있습니다.
개발 관련 지식은 기초적인 수준으로, 사용 가능한 개발언어는 Flutter(기초), Python(데이터 분석 및 자동화 수준) 정도입니다.
이 프로젝트의 목적은 "정말로 한 명이 기초적인 개발 지식을 가지고 앱서비스를 만들고 런칭할 수 있는가?"를 검증해보기 위함입니다.
그렇기 때문에 어느 정도 알고 있는 언어를 사용하지는 않고 온전히 AI에서 제공하는 가이드를 따라 생성을 진행할 예정입니다.
이 프로젝트에서 사용하는 AI 서비스 환경은 다음과 같습니다.
- ChatGPT Plus
- Github Copilot Pro
- Perplexity Pro
Copilot Pro를 사용하기 위해 IDE는 Visual Studio Code를 사용합니다.
기본 구조
기본적으로, ChatGPT의 프로젝트 기능을 사용하여 다양한 기획, 개발의 기반이 되는 문서들을 업로드하고 이 문서들을 RAG 방식으로 활용하여 개발을 진행할 예정입니다.
단, Copilot에서는 ChatGPT의 프로젝트 파일을 활용할 수 없으므로 별도 폴더에 문서들을 넣어두고 필요할 때마다 참조하여 개발하도록 합니다.

1단계 - 기초 문서 만들기
최초에 서비스가 어떠한 핵심 기능을 할 것인지 정도의 간단한 니즈만 가지고 초기 문서를 생성합니다.
여기에서는 ChatGPT의 "심층 리서치" 기능을 사용합니다.
[심층 리서치 프롬프트]
여행에서 찍은 사진을 정리하고, 다른 사람에게 내용을 공유하는 서비스를 만들고 싶어.
이 서비스에 대한 핵심 기능은 다음과 같아.
[핵심 기능]
- 업로드한 여행 사진들의 촬영 시간과 위치 데이터를 분석하여 시간선 및 위치에 따른 사진 그룹 짓기
- 각 그룹별로 방문지, 방문지에 대한 평가를 남기기
- 이 여행을 시간순, 공간순으로 정렬하고 각 그룹의 평가를 포함하여 여행 전체를 한 눈에 볼 수 있도록 정리하기
- 정리된 내용을 기반으로 LLM 생성형 AI를 사용하여 여행 전체를 정리하는 기록을 만들기
- 만들어진 기록을 SNS에 공유하기
1인 개발자로써, 혼자 서비스를 개발하기 위해 서비스 기획과 개발에 필요한 인프라 구성 등의 모든 기초 작업 준비를 해 줘
보다 내용을 구체화하기 위한 추가 질문 후에, 다음과 같은 멋진 문서를 만들어주었습니다.
이 내용을 검토하여 제가 최초에 기대했던 것 혹은 상상했던 것과 차이가 있는지 확인합니다. 다행히, 결과물에서 큰 차이는 없어 보입니다. 내용이 길어 접어두었으므로, 자세한 내용이 보고 싶으시면 펼쳐서 읽어봐주세요.
여행 사진 정리 및 SNS 공유 웹 서비스: 기획 및 아키텍처 제안
서비스 개요
1인 개발자가 손쉽게 여행 사진을 웹에서 정리하고 자동으로 여행 기록을 생성하여 SNS에 공유할 수 있는 웹 기반 서비스입니다. 사용자는 스마트폰으로 촬영한 원본 사진들(JPEG/HEIC 등)을 업로드하면, 사진의 EXIF 메타데이터(촬영 시간, GPS 위치)를 활용하여 자동으로 시간대별·장소별 그룹화를 수행합니다. 각 그룹에 대해 사용자가 방문지 명칭과 간단한 평가(후기나 별점 등)를 입력하면, 서비스가 전체 여행 일지를 시간 순으로 정리하고 생성형 AI를 통해 마치 여행 일기처럼 서술된 여행 기록 텍스트를 자동 생성해 줍니다. 사용자는 이 자동 생성된 글을 필요에 따라 편집한 뒤, SNS에 공유하여 다른 사람들과 여행 추억을 쉽게 나눌 수 있습니다.
핵심 기능
- EXIF 기반 사진 그룹화: 업로드된 여행 사진들의 촬영 시간과 **위치 정보(GPS)**를 읽어와, 일정 시간 범위 내이면서 장소가 가까운 사진들을 하나의 이벤트로 타임라인(시간선) 및 위치별 그룹화합니다
. 예를 들어, 같은 날 같은 지역에서 촬영된 사진들은 한 그룹으로 묶습니다. - 그룹 별 방문지명 및 평가 입력: 자동으로 생성된 각 사진 그룹마다 사용자가 방문한 장소 이름과 해당 장소에 대한 **평가(리뷰 또는 평점)**를 입력할 수 있는 기능을 제공합니다. 이를 통해 사진 묶음에 의미를 부여하고 개인적인 소감을 추가합니다.
- 여행 전체 기록 및 정렬: 모든 그룹을 시간 및 장소 순서대로 정렬하여 하나의 여행 타임라인을 구성합니다. 예를 들어 Day1 어디서 무엇을 했고, Day2 어디를 방문했는지가 순차적으로 정리됩니다. 각 그룹에는 사용자가 입력한 방문지명과 평가가 포함되어, 전체 여행 일지 형태로 묶입니다.
- 생성형 AI 기반 여행기 텍스트 생성: 정리된 여행 타임라인 데이터를 바탕으로 **대규모 언어 모델(LLM)**을 활용한 여행기 자동 생성 기능을 제공합니다. 즉, 각 날의 일정과 느낌을 종합하여 AI가 마치 사람의 여행 일지처럼 서술형 문장을 생성합니다. 예를 들어 “첫째 날 우리는 에펠탑을 방문했고 매우 인상적이었다”와 같이, 사용자 사진과 입력 정보를 근거로 한 문장이 생성됩니다.
- 편집 및 SNS 공유: 자동 생성된 여행 기록 텍스트를 사용자가 필요에 따라 편집할 수 있는 에디터 기능과, 최종 확정된 여행기를 손쉽게 SNS에 공유할 수 있는 기능을 제공합니다. 공유 시 여행기의 텍스트와 대표 사진 등이 포함된 공유용 페이지가 생성되고, 사용자는 링크를 복사하거나 Web Share API 등을 통해 페이스북, 트위터, 인스타그램 등으로 손쉽게 보낼 수 있습니다
.
시스템 아키텍처 및 기술 스택
전체 시스템은 클라이언트 중심으로 동작하여, **프론트엔드(클라이언트)**와 백엔드(서버), 그리고 외부 AI 요약 API로 구성됩니다. 1인 개발자가 효율적으로 개발/운영할 수 있도록 **서버리스 및 BaaS(Backend as a Service)**를 적극 활용합니다. 아래는 권장 구성 요소와 기술 스택입니다:
- 프론트엔드 (웹 클라이언트): 사용자의 웹 브라우저에서 동작하는 싱글 페이지 애플리케이션(SPA) 또는 프로그레시브 웹앱(PWA) 형태로 구현합니다. React 프레임워크 기반의 Next.js를 사용하면 개발 생산성과 성능을 모두 확보할 수 있습니다. Next.js를 통해 UI를 구성하고, 페이지 간 내비게이션, 상태 관리 등을 처리합니다. 스타일링은 Tailwind CSS나 Chakra UI 등의 라이브러리를 활용하여 모바일 친화적인 반응형 웹 디자인으로 구현합니다. 사진 파일 선택/업로드, 미리보기, 텍스트 편집 등의 풍부한 기능을 UI 상에서 제공할 것입니다.
- EXIF 메타데이터 처리 (클라이언트): 사진 이미지를 업로드하면 클라이언트 측에서 EXIF 메타데이터를 추출합니다. 사진 파일은 <u>사용자 단말기 내에서 처리되며 서버로 원본이 전송되기 전에 메타데이터를 분석</u>합니다. 이를 위해 JavaScript EXIF 파서 라이브러리(예: ExifReader 또는 exif-js)를 사용합니다. 실제 사례로, 한 개발자는 웹 브라우저 상에서 선택된 이미지의 EXIF 데이터를 파싱하기 위해 ExifReader 라이브러리를 활용하여 모든 처리를 클라이언트에서 수행한 바 있습니다
. 이 단계에서 얻은 촬영 시간(DateTimeOriginal)과 GPS 좌표(GPSLatitude, GPSLongitude) 정보를 배열로 저장합니다. 클라이언트는 이러한 메타데이터를 기반으로 즉각적으로 사진 그룹화 연산을 수행합니다 (자바스크립트로 구현). - 사진 그룹화 로직: 그룹화는 시간 간격과 공간적 거리 두 가지를 고려합니다. 일정 시간(예: 2~3시간) 이내에 촬영되고 GPS 좌표상 서로 가까운 사진들은 한 그룹으로 묶습니다. 구현상으로는 시간값(타임스탬프) 차이와 위치 좌표 간 거리를 계산하여, 두 사진 사이의 “이벤트 거리”를 정의하고 임계값 이하이면 동일 이벤트로 간주합니다
. 예를 들어 연속된 날들에 걸쳐 촬영되었지만 장소 이동이 거의 없는 경우 하나의 연속된 이벤트로 묶고, 촬영 날짜 사이에 긴 공백이 있거나 촬영 장소가 수백 km 떨어져 있으면 별개의 그룹으로 분리합니다. (필요 시 k-means 등의 클러스터링 기법을 적용하여 자동 그룹 개수를 산정할 수도 있습니다.) 그룹화 결과는 클라이언트 메모리 상에 구조화된 데이터(예: Trip -> Day 1 -> [장소1 그룹, 장소2 그룹] -> Day 2 -> ...)로 유지됩니다. - 백엔드 서버 및 API: 백엔드는 서버리스 함수 또는 경량 서버 형태로 구성하여 주로 AI 요약 요청 처리와 데이터 영속화를 담당합니다. Next.js를 사용할 경우 내장 API Routes를 통해 서버리스 함수들을 구현할 수 있고, 별도의 백엔드 프레임워크로 Node.js + Express를 사용하여 REST API를 구성하는 것도 가능합니다. 1인 개발자의 운영 부담을 줄이기 위해, Vercel과 같은 플랫폼에 이 백엔드 기능을 배포하면 자동으로 확장 가능한 서버리스 API가 됩니다. Vercel 플랫폼은 프론트엔드 배포와 함께 서버리스 함수를 제공하고 글로벌 CDN, 자동 배포 등을 지원하여 서버 관리의 복잡성을 크게 줄여줍니다
. 백엔드는 주요 기능으로:- 업로드된 원본 사진 저장 (필요 시): 사용자가 사진 업로드를 완료하면, 선택적으로 사진 파일들을 클라우드 스토리지에 업로드하는 API를 제공합니다. (EXIF는 이미 추출되었으므로 이미지 파일은 이때 전송) 대용량 이미지를 효율적으로 다루기 위해 업로드 전에 클라이언트에서 이미지 리사이징/압축(canvas나 browser-image-compression 활용)하거나, 서버에서 수신 후 썸네일 생성 작업을 비동기로 처리할 수 있습니다.
- OpenAI API 연동: 여행 타임라인과 사용자 입력 정보를 받아 OpenAI 등의 LLM API에 요청을 보내 여행기 텍스트를 생성합니다. 예를 들어 OpenAI의 GPT-4 모델을 사용하여 프롬프트에 "다음 여행 일정과 장소 평가를 참고하여 한국어 여행 후기를 작성해줘"와 같은 지침과 함께 날짜별 방문지와 메모를 전달하면, 모델이 자연스러운 서술형 텍스트를 반환합니다. 이 요청은 백엔드에서 발생하며, API 키 등의 보안 정보는 백엔드에 저장되어 안전하게 호출됩니다.
- 데이터베이스 연동: 사용자의 여행 기록(그룹화된 구조 및 생성된 텍스트 등)을 저장하고 불러오는 API를 제공합니다. 이를 통해 사용자가 편집을 완료한 최종 여행기를 DB에 저장하고, 고유 URL로 공유할 수 있게 합니다.
- SNS 공유용 페이지 제공: 특정 여행 기록을 식별하는 ID 또는 슬러그를 받아 그 내용(텍스트 및 사진 URL 등)을 응답하는 API 혹은 서버사이드 렌더링 페이지를 제공합니다. 이를 통해 다른 사용자가 공유 링크에 접속하면 해당 여행기의 내용을 웹 페이지로 볼 수 있습니다.
- 데이터베이스 및 스토리지: 사용자별 여행 기록 데이터(예: 여행 제목, 각 방문지 명, 평가, 생성된 본문 텍스트 등)는 데이터베이스에 저장합니다. 1인 개발 환경에서 관리가 용이하도록 **Managed DB(BaaS)**를 사용합니다. 예를 들어 Supabase를 선택하면, PostgreSQL 기반의 호스팅 DB를 사용하면서 자동으로 RESTful API와 인증, 파일 스토리지 등을 활용할 수 있습니다
. Supabase는 Firebase의 대안으로, PostgreSQL에 기반한 저장소, 인증, 실시간 구독, 서버리스 함수까지 백엔드에 필요한 기능을 제공하므로, 복잡한 서버 구축 없이도 데이터 관리를 할 수 있습니다 . 대안으로 NoSQL이 편하다면 Firebase Firestore를 사용할 수도 있습니다. 이미지 파일 저장소는 Supabase를 사용할 경우 내장 **스토리지(bucket)**에 업로드하며, Firebase의 경우 Firebase Storage 또는, 일반 클라우드 스토리지(AWS S3 등)를 사용할 수 있습니다. 저장된 이미지는 추후 공유 페이지에서 불러오거나 썸네일로 활용됩니다. (HEIC처럼 웹 호환성이 낮은 포맷의 경우 서버 업로드 시 JPEG 변환을 적용하여 저장하면, 모든 브라우저에서 볼 수 있도록 처리 가능합니다.) - 생성형 AI 요약 서비스: 본 서비스의 핵심 차별점인 여행기 자동 생성은 OpenAI와 같은 외부 생성형 AI API를 활용합니다. OpenAI의 GPT-3.5/GPT-4 모델을 사용하면 사람처럼 유창한 여행 후기를 생성할 수 있습니다. 백엔드 서버에서 AI API를 호출하며, API 응답으로 생성된 텍스트를 받아 클라이언트에 전달합니다. 향후 오픈소스 LLM을 직접 호스팅하는 방향으로도 확장 가능하지만, 초기에는 OpenAI API 활용이 개발 편의와 문장 품질 측면에서 유리합니다. AI 요약 서비스 모듈은 추후 프롬프트 엔지니어링을 통해 사용자 평가(예: “맛있었다”, “별로였다” 등의 감상)를 텍스트에 잘 녹여내도록 조정합니다.
- 기술 스택 요약: 요약하면 프론트엔드는 TypeScript + React(Next.js) 기반으로 동작하고, 백엔드는 Node.js 서버리스 함수(또는 경량 서버)로 OpenAI API를 호출하며, 데이터 저장은 **Supabase(Postgres)**와 **스토리지(S3 호환)**를 활용하는 구성이 됩니다. 이러한 조합은 1인 개발자가 전체 시스템을 End-to-End로 구현하기에 충분하며, 필요시 Vercel + Supabase/Firebase 조합만으로도 별도 서버 관리 없이 모든 기능을 구현할 수 있습니다 (1인 개발의 경우 해당 조합으로 인프라 지식 없이도 충분히 서비스 제작이 가능함이 커뮤니티에서도 언급되고 있습니다).
사용자 플로우 및 페이지 구성
서비스의 주요 사용 시나리오는 다음과 같으며, 이에 따라 페이지 및 화면 흐름을 설계합니다:
- 사진 업로드 페이지: 사용자 로그인 또는 진입 후 첫 화면에서 여행에서 촬영한 여러 장의 사진을 선택하여 업로드합니다. 모바일 사용자를 고려하여 "파일 선택" 버튼 외에도 드래그앤드롭 영역이나 모바일에서는 갤러리/카메라 열기 기능을 지원합니다. 선택된 사진들은 썸네일로 미리보여주고, 사용자가 "업로드 시작"을 누르면 클라이언트 측에서 EXIF 정보를 읽어옵니다. 이때 다량의 사진도 빠르게 처리될 수 있도록 비동기로 진행하며, 진행 상태(예: “메타데이터 분석 중…”)를 사용자에게 표시합니다.
- 사진 메타데이터 분석 및 그룹화: 업로드된 사진들의 EXIF 데이터를 클라이언트에서 추출한 뒤, 즉시 그룹화 알고리즘을 적용합니다. 이 결과로 여행 기간 동안의 타임라인이 생성됩니다. 예를 들어 “Day 1 - 2025/04/01” 아래에 오전, 오후 등의 하위 그룹이 생길 수 있고 각 그룹에 해당하는 사진들이 연속으로 배열됩니다. UI 상에는 시간순 그룹별로 사진 묶음이 표시되고, 각 묶음의 대표 사진, 촬영기간, 촬영장소(좌표 -> 지도 API를 통해 지명 변환 시 미리 채울 수 있음) 등을 요약 보여줍니다. 사용자가 필요하면 그룹을 분할하거나 합칠 수 있는 옵션도 제공합니다 (예: 그룹 자동 분류가 부정확한 경우 수동 조정).
- 그룹 정보 입력 페이지: 그룹화가 완료되면, 사용자에게 각 그룹별로 **“방문지 이름”과 “간단한 평가(후기)”**를 입력받는 폼을 표시합니다. 예를 들어 첫 번째 그룹에 대해 “에펠탑 - 매우 아름답고 인상적이었다 (5/5)”와 같이 장소명과 느낌을 적을 수 있습니다. 만약 특정 그룹의 위치 좌표가 존재한다면 좌표를 역지오코딩하여 “파리 에펠탑”처럼 추천 장소 이름을 제공하고 사용자가 편집하도록 할 수도 있습니다 (이때 Google Maps Geocoding API 등 이용). 각 그룹 카드에는 그 장소를 나타내는 지도 썸네일(선택사항, Google Static Maps API 등 활용 가능)이나 대표 사진도 보여주어 맥락을 제공합니다. 사용자가 모든 그룹에 이름/메모 입력을 마치면 “여행 일지 생성” 버튼을 눌러 다음 단계로 넘어갑니다.
- 여행 기록 생성 (AI 요약): 사용자가 여행 타임라인과 그룹별 메모 입력을 완료하고 나면, 백엔드에 여행 요약 생성 요청을 보냅니다. 이때 프론트엔드에서는 로딩 인디케이터(예: “AI가 여행 기록을 작성하고 있습니다…”)를 띄워주고, 백엔드 API 호출을 진행합니다. 백엔드는 앞서 설명한 대로 OpenAI 등의 LLM API에 이 정보를 전달하고 서술형 여행기 텍스트를 생성합니다. 몇 초 가량 소요된 후 백엔드가 완성된 여행기 내용을 응답하면, 프론트엔드는 결과를 받아 다음 화면에 표시합니다.
- 여행기 확인 및 편집 페이지: 생성된 여행 기록 텍스트를 사용자에게 보여주는 페이지입니다. 이 페이지에서는 텍스트 편집 기능을 제공하여 AI 생성 내용에 사용자가 수정을 가하거나, 추가로 하고 싶은 말을 덧붙일 수 있게 합니다. 간단한 WYSIWYG 에디터나 리치텍스트 에디터를 사용하여 글자 스타일, 이모지, 줄바꿈 등을 편집할 수 있으며, 원하는 경우 각 문장 옆에 원본 사진을 배치하거나 그룹 구분을 넣는 등 편집도 가능하게 합니다. 기본적으로는 AI가 만들어준 본문이 일기 형식으로 쭉 나열되어 있고, 날짜별 구분이나 하이라이트는 자동으로 서식화합니다. 예를 들어 날짜 제목은 볼드체 큰 글씨로, 장소명은 이탤릭 처리 등 시각적 구성을 가미합니다. 사용자는 여행 전체 내용을 확인하면서 잘못된 정보가 있으면 직접 수정한 후, 최종 확정합니다.
- SNS 공유 및 저장: 사용자가 여행기 편집을 완료하면, “공유하기” 버튼을 눌러 결과를 세상과 공유합니다. 공유하기를 누르는 순간, 프론트엔드는 해당 여행기의 모든 데이터를 백엔드로 전송하여 데이터베이스에 저장합니다. 백엔드는 이 여행 기록에 대한 고유 ID(예: UUID 혹은 짧은 해시)를 발급하고, 이를 기반으로 접근 가능한 공유용 페이지 URL을 생성합니다 (예: https://mytravel.app/share/abcd1234). 이 URL은 DB에 저장된 여행기 ID에 매핑되어 있어, 누구나 링크를 클릭하면 해당 콘텐츠를 볼 수 있습니다. 공유 페이지에는 여행기 제목, 작성자(옵션), 본문 텍스트, 대표 사진들이 포함됩니다. 또한 원활한 공유를 위해 Open Graph 태그를 설정하여 SNS에서 미리보기 카드(제목, 썸네일 이미지, 요약)가 보이도록 합니다. 공유 방법으로는 단순히 링크를 복사해갈 수도 있고, 모바일 기기에서는 Web Share API를 활용하여 OS 레벨의 공유 시트를 열 수 있습니다
. Web Share API를 사용하면 제목, 텍스트, URL을 간단히 지정하여 사용자가 설치한 다른 SNS 앱으로 직접 공유할 수 있으므로 모바일 웹에서 편리합니다 . (만약 Web Share API를 지원하지 않는 환경이라면, 트위터나 페이스북 공유 링크 생성 등의 폴백을 제공합니다.) 이렇게 공유된 여행 기록 페이지는 읽기 전용으로 제공되며, 작성자만 수정할 수 있습니다. - 공유된 여행 기록 열람: 다른 사용자가 SNS에서 공유된 링크를 클릭하면, 해당 여행 기록의 공개 페이지가 렌더링됩니다. 이 페이지는 서버 사이드 렌더링(SSR) 또는 정적 페이지로 제공되어, 로딩 시 즉시 콘텐츠가 표시되고 SEO에도 걸리도록 합니다. 방문자는 여행 일지 텍스트와 사진들을 스크롤하며 볼 수 있고, 반응형 디자인으로 모바일에서도 보기 좋게 구성합니다. 필요하다면 이 페이지에서 “나도 내 여행기 만들어보기” 같은 **콜투액션(CTA)**을 넣어 서비스 신규 유입을 유도합니다.
각 페이지는 위 흐름에 맞게 Next.js의 라우트로 구현할 수 있습니다. 예를 들어 /upload (사진 업로드), /grouping (자동 그룹 확인 및 그룹별 정보 입력), /story (AI 생성 글 확인/편집), /share/[id] (공유용 공개 페이지) 등의 구조로 만들어집니다. 페이지 전환 간에는 상태를 유지해야 하므로, Zustand이나 Redux 같은 상태관리, 또는 URL에 일시적인 세션 토큰을 부여해 서버에 임시 저장하고 다음 페이지에서 불러오는 방법도 고려합니다. 네비게이션 바에는 서비스 이름과 간단한 설명, 그리고 (로그인 기능이 있다면) 사용자 프로필 등이 표시되고, 각 단계별로 진행 상황을 표시하는 progress bar나 스텝 표시를 두어 사용자 경험을 향상시킵니다.
개발 및 배포 환경 제안
- 개발 환경: 로컬에서는 Node.js 기반으로 Next.js 개발 서버를 띄워가며 프론트/백엔드를 한꺼번에 개발합니다. TypeScript를 사용하여 타입 안정성을 높이고, VSCode 등의 에디터와 ESLint/Prettier를 적용해 코드 품질을 유지합니다. Git 저장소로 버전관리를 하며, 주요 브랜치에 푸시하면 자동으로 배포가 이루어지도록 CI/CD를 구성합니다.
- 배포 플랫폼: Vercel을 추천합니다. Vercel은 Next.js 프로젝트와 극대화된 연동성을 갖고 있어, 레포지토리를 연결하면 푸시 시 자동 빌드/배포됩니다. 프론트엔드 정적 파일은 전세계 CDN에 배포되고, API Routes로 작성된 백엔드 함수는 서버리스로 자동 호스팅됩니다
. 1인 개발자는 복잡한 서버 설정 없이도 Vercel 대시보드에서 환경변수 관리(예: OpenAI API 키), 도메인 연결, 모니터링 등을 할 수 있습니다. 대안으로 Netlify도 유사한 기능을 제공하며, Next.js 외에 React SPA의 경우 CloudFront+S3에 정적 호스팅하고 AWS Lambda로 API를 구성하는 방안도 있지만, 이는 운영 부담이 커지므로 Vercel 같은 올인원 플랫폼이 적합합니다. - 데이터베이스 및 인증: Supabase를 사용하면 배포가 용이합니다. Supabase 프로젝트를 생성한 뒤, 프론트엔드에서 Supabase JS SDK를 통해 손쉽게 Postgres DB에 쿼리하거나 스토리지에 파일 업로드를 할 수 있습니다. Supabase는 테이블 스키마만 정의하면 자동으로 RESTful 엔드포인트가 생기고, 필요한 곳에 RLS(Row-Level Security)나 OAuth 인증 등을 붙일 수 있어 유연합니다. 개발 단계에서는 Supabase의 로컬 Docker 환경을 활용해 볼 수도 있고, Firebase를 쓴다면 Firebase Emulators로 개발 환경을 꾸립니다. 환경 변수 (예: Supabase URL/키, Firebase API 키 등)는 .env 파일에 저장하고 Vercel 환경설정에 주입합니다.
- AI API 연동: OpenAI API는 백엔드(Node.js)에서 openai 라이브러리나 axios로 HTTPS 호출을 통해 사용합니다. OpenAI API 키는 노출되지 않도록 서버 환경변수로만 유지하고, 백엔드 코드는 이 키를 이용해 GPT-4 (또는 3.5 Turbo) 모델의 completion/chat endpoint를 호출합니다. 프롬프트 구성과 모델 선택은 실험을 통해 최적화하고, 생성된 텍스트의 길이에 따라 토큰 수나 비용이 증가하므로 필요하면 일정 길이로 요약하도록 프롬프트를 조정합니다. 요금 관리를 위해 OpenAI의 사용량 대시보드 모니터링을 하고, 무료 대안으로 HuggingFace Hub의 번역/요약 모델 API를 고려할 수도 있습니다.
- 이미지 및 정적 파일 호스팅: 사용자가 업로드한 원본 사진은 Supabase 스토리지 또는 Firebase Storage에 저장됩니다. Supabase의 경우 업로드된 파일에 대해 퍼블릭 URL을 부여하거나 공유 페이지 요청 시 서버가 프록시로 전달할 수 있습니다. Supabase 스토리지는 이미지 변환 기능을 제공하여, URL 파라미터로 리사이즈된 이미지를 서빙하는 등 자동 최적화가 가능합니다
. 이를 활용해 공유 페이지에서 원본의 썸네일(저용량) 이미지를 불러와 빠르게 표시하고, 클릭 시 고해상도 원본을 띄우는 식으로 최적화합니다. 정적 파일(예: 서비스 로고, 아이콘 등)은 Vercel에 함께 배포하거나, 별도 CDN에 올려도 무방합니다. - 배포 워크플로우: GitHub 등의 레포지토리와 Vercel/Supabase를 연동해 CI/CD 파이프라인을 구축합니다. main 브랜치에 코드를 푸시하면 Vercel이 자동으로 최신 코드를 빌드하여 프로덕션에 반영하고, Supabase의 경우 DB 스키마 변경은 마이그레이션 SQL을 코드에 포함시켜 supabase migrate로 배포하거나 대시보드에서 수동 적용합니다. 코드 변경이 빈번하지 않은 1인 프로젝트 특성상 배포 자동화에 큰 장애는 없으며, 필요 시 GitHub Actions로 테스트 -> 빌드 -> 배포 과정을 제어할 수도 있습니다.
필요한 서드파티 라이브러리 및 API 목록
- ExifReader (JavaScript 라이브러리) – 클라이언트 측에서 사진의 EXIF 메타데이터(촬영 시간, GPS 등)를 파싱하여 획득
. (대안: exif-js) - OpenAI API (GPT-3.5/GPT-4) – 여행 기록 자동 생성을 위한 생성형 AI API. 백엔드에서 호출하여 사용자의 여행 타임라인을 서술형 텍스트로 변환. OpenAI의 공식 Node.js 라이브러리(openai 패키지)나 REST API를 통해 통신.
- Supabase (호스팅 BaaS) – PostgreSQL 기반 데이터베이스, 파일 스토리지, 인증 등을 제공하는 BaaS
. JS용 SDK (@supabase/supabase-js)로 손쉽게 데이터 입출력 및 파일 업로드 구현. (대안: Firebase – Firestore DB, Firebase Storage, Firebase Auth 등) - React & Next.js 프레임워크 – 사용자 인터페이스 구축과 라우팅, 서버리스 함수(API Routes) 구현에 사용. SSR/SSG 지원을 통해 공유 페이지 SEO 개선.
- UI 컴포넌트 라이브러리 – 신속한 UI 개발을 위해 사용. 예: **Material-UI(MUI)**의 Timeline 컴포넌트 (수직 타임라인 구현에 활용), Chakra UI 또는 Ant Design 등으로 폼, 버튼, 모달 등 제작.
- 지도 및 위치 API – (옵션) 사진의 GPS좌표를 실제 지명으로 변환하기 위해 Google Maps Geocoding API 또는 Mapbox Geocoding API를 사용. 또한 공유 페이지나 그룹 정보 입력 시 지도 화면 표시에는 Leaflet + OpenStreetMap 같은 오픈소스 지도나 Google Maps Embed API를 활용 가능.
- 웹 공유 API – 모바일 환경에서 네이티브 공유 기능을 호출하기 위한 표준 Web API. navigator.share() 메소드를 통해 제목, 텍스트, URL 등을 다른 앱으로 전달
. (폴백으로 카카오 SDK나 Facebook SDK 등을 통한 직접 공유도 고려) - 이미지 처리 라이브러리 – (옵션) 클라이언트에서 이미지 리사이즈/압축에 browser-image-compression 같은 패키지 사용. 서버에서 썸네일 생성 시 sharp (Node.js 이미지 처리 라이브러리) 사용 가능.
- 기타 개발 도구 – 형상관리를 위한 Git 및 GitHub, 배포를 위한 Vercel CLI, 테스트를 위한 Jest/React Testing Library, 오류 모니터링을 위한 Sentry 등이 프로젝트에 포함될 수 있습니다.
확장성 고려 및 운영 자동화 방향
- 서버리스 및 자동 확장: 제안된 구조는 서버리스 기반이므로 사용자가 증가해도 자동으로 확장됩니다. 프론트엔드는 정적 파일로 배포되어 대규모 트래픽에도 CDN이 대응하고, 백엔드 API는 호출량에 따라 Vercel이 함수 인스턴스를 늘려 처리합니다. 개발자는 명시적으로 서버 증설을 하지 않아도 되며, Supabase/Firebase 역시 매니지드 서비스로서 일정 수준까지 자동 확장됩니다. 데이터베이스의 경우 연결 수나 질의를 고려하여 플랜을 업그레이드하는 것으로 수직 확장 가능하고, 필요하면 읽기 복제본을 추가하여 확장성을 높일 수 있습니다.
- 이미지 최적화와 CDN: 사진 서비스에서 이미지 로딩 최적화는 중요합니다. Supabase 스토리지의 이미지 트랜스포메이션 기능을 활용하면 요청 시 파라미터로 리사이즈된 이미지 URL을 생성할 수 있어, 별도의 이미지 썸네일 생성 코드 없이도 저해상도 이미지 빠른 제공이 가능합니다
. 이를 통해 모바일에서도 빠르게 사진 미리보기를 표시하고, 원본은 필요시에만 불러오도록 하여 트래픽을 절약합니다. Firebase Storage를 쓸 경우 Cloud CDN을 앞단에 설정해 캐싱하도록 하고, 또는 Cloudinary 같은 서비스를 도입해 업로드와 동시에 여러 사이즈로 변환해두는 방안도 있습니다. 모든 이미지 파일과 정적 자원에 적절한 캐시 만료 헤더를 설정해 브라우저 캐싱을 활용하고, CDN을 통한 지리적 캐시 분산으로 응답 시간을 최소화합니다. - 코드 및 인프라 관리 자동화: Vercel과 GitHub 연동으로 Continuous Deployment가 구현되어, 코드 푸시 시 자동 빌드/배포가 되어 별도의 수동 배포가 필요 없습니다. 또한 Vercel은 자동으로 프리뷰 배포를 제공하므로, 기능 추가/수정 시마다 미리보기 URL로 결과를 검증할 수 있어 개발 효율이 높습니다. 인프라 구성은 대부분 서비스형으로 처리되므로, 코드 레벨에서 IaC(Infrastructure as Code)를 관리할 내용은 최소입니다. 다만 Supabase의 DB 스키마는 supabase/migrations에 SQL로 버전관리를 해두고, 이를 통해 로컬과 프로덕션의 스키마 동기화를 자동화합니다.
- 모니터링 및 로깅: 1인 개발이라도 로그 수집과 모니터링을 설정해 두는 것이 안정성에 도움이 됩니다. Vercel 제공 함수 로그와 Supabase 로그를 수시로 확인할 수 있고, 중요 오류에 대해서는 알림을 받도록 Sentry와 같은 APM(Application Performance Monitoring)을 붙일 수 있습니다. 예를 들어 OpenAI API 호출 실패나 시간 초과 시 에러를 로깅하고 개발자에게 이메일 알림을 보낸다든지, 사용자 행동 로그(업로드 시작~종료, 공유 횟수 등)를 분석해 대시보드로 시각화하면 서비스 운영에 유용합니다.
- 비용 관리 및 확장 계획: 초기에는 대부분의 구성요소가 프리 티어 또는 저비용으로 운영될 수 있습니다. 사용량이 증가하면 OpenAI API 비용이 가장 크게 증가할 수 있으므로, 필요시 캐싱 전략(동일 여행기 재생성 방지 등)이나 대체 모델 활용을 고려합니다. 또한 사용자 증가에 따라 Supabase 요금제가 올라갈 수 있으므로, 일정 임계점에서는 자체 서버/DB로 마이그레이션하는 계획도 수립합니다. 하지만 제안된 아키텍처는 벤더 종속을 최소화하여, Next.js 코드는 어디서든 호스팅 가능하고 데이터는 PostgreSQL 호환되어 이전이 비교적 수월합니다.
- 백업 및 복구: 서비스 운영 중 사진 및 글 데이터의 백업 전략도 필요합니다. Supabase Postgres의 자동 백업 기능을 활성화하고 주기적으로 별도 저장소에 덤프를 받아 놓거나, Firebase라면 Firestore 백업 스케줄링을 설정합니다. 이미지 파일도 주기적으로 별도 스토리지(예: AWS S3로 복제)하거나, 사용자 단말에 그대로 있으므로 필요시 재업로드 할 수 있게 안내합니다.
- 향후 기능 확장: 구조적으로 확장에 대비하여 모듈화를 염두에 둡니다. 예를 들어 사용자 인증(로그인/회원가입)을 초기 버전에선 생략하거나 간단히 소셜 로그인으로 넣되, 추후 확장할 수 있도록 Auth 모듈을 분리해 두고, 여행기 공유 범위(공개/비공개) 설정 기능을 추가할 수 있습니다. 또한 AI 기능도 발전하는 대로 여행 요약 외에 사진 캡션 생성, 감정 분석 등 부가 기능을 붙이기 쉬운 형태로 설계합니다. 이러한 기능 추가시에도 기존 아키텍처에 큰 무리 없이 서버리스 함수 하나 추가, 테이블 하나 추가 등의 방식으로 개발할 수 있어야 합니다.
以上の 제안으로, 1인 개발자가 웹 기반의 여행 사진 정리 및 공유 서비스를 효과적으로 구축하고 운영할 수 있을 것입니다. 제안된 아키텍처는 클라이언트 성능 최적화와 생성형 AI의 효과적 활용, 그리고 클라우드 서비스의 자동화에 중점을 두어, 최소한의 노력으로 사용자에게 높은 편의성을 제공하는 것을 목표로 합니다. 필요한 기술 스택과 인프라를 적절히 활용하면, 혼자서도 충분히 확장성 있고 유지보수 쉬운 서비스를 만들어 낼 수 있을 것입니다.
참고 자료:
- EXIF 메타데이터를 브라우저에서 처리하는 사례
- 시간·위치 기반의 사진 그룹화 알고리즘 논의
- Supabase (오픈소스 BaaS) 및 Vercel 플랫폼 소개
- Web Share API를 통한 웹 콘텐츠 공유 방법
- Supabase 스토리지의 이미지 실시간 최적화 기능
기획 문서 만들기
이러한 기초적인 니즈 및 환경적인 배경을 기반으로 기획 문서를 만듭니다.
저는 기본적으로 유저 스토리를 만들고, 그 유저 스토리를 만족시키는 인수 조건과 정책을 만드는 방향으로 기획을 합니다.
그렇기 때문에, 동일한 방향으로 기획 문서를 만들어냅니다.
또한, 모처럼 AI를 사용하므로 이 정책과 조건들을 코딩으로 만들기 좋도록 알고리즘도 함께 만들어봅니다.
[기획 문서 생성 프롬프트]
너는 Product Manager야. 이전에 받은 답변을 기반으로, 아래의 작업을 완수해 줘.
1. 사용자 플로우 및 페이지 구성을 기반으로 각 페이지에서의 User Story와 인수조건을 작성해 줘.
2. 각 인수조건을 개발자가 보고 이해할 수 있도록 정책과 알고리즘을 작성해줘
이에 따라 아래와 같은 기획 문서를 만들어 주었습니다.
해당 내용을 검토했을 때, 큰 문제가 없으므로 이 상태로 기획 문서 초안을 저장합니다.(더보기를 누르시면 자세한 내용을 보실 수 있습니다)
1. 사용자 플로우 및 페이지 구성: User Story & 인수 조건
(1) 사진 업로드 페이지
- User Story
- “여행을 다녀온 사용자로서, 스마트폰으로 찍은 사진들을 한 번에 업로드하여, 시간/위치 데이터를 빠르게 분석할 준비를 하고 싶다. 업로드가 편리하고 상태를 확인하기 쉬워야 한다.”
- 목적: 사진을 업로드하여 이후의 메타데이터 추출과 그룹화가 가능한 상태가 되는 것
- Acceptance Criteria
- [AC1.1] 업로드 파일 형식 및 용량 검증
- 파일을 드래그&드롭 혹은 ‘파일 선택’ 버튼으로 업로드할 때, JPG/HEIC 등 지원되는 확장자만 허용해야 한다.
- 각 파일의 용량이 설정된 최대치(예: 10MB)를 초과하면 경고 메시지를 표시하고 업로드를 취소한다.
- [AC1.2] EXIF 메타데이터 추출
- 업로드된 사진을 클라이언트 측에서 EXIF 라이브러리(exif-js, ExifReader 등)를 통해 분석한다.
- EXIF 정보(촬영 일시, GPS 좌표 등)가 존재하면 성공적으로 파싱했다는 표시(썸네일 혹은 아이콘)를 보여주고, 없는 파일은 경고 표시를 한다.
- [AC1.3] 업로드 진행 상황 및 오류 피드백
- 업로드되는 동안 진행 상태(예: 업로드 진행률, ‘메타데이터 추출 중…’)가 표시되어야 한다.
- 오류 발생 시, 오류 메시지를 사용자에게 즉시 표시한다 (예: “업로드 실패”, “지원하지 않는 파일 형식입니다” 등).
- [AC1.4] 다음 단계 이동
- 모든 파일 검증과 EXIF 파싱을 마치면, “다음으로” 버튼이 활성화되어 그룹화 페이지로 이동할 수 있다.
- [AC1.1] 업로드 파일 형식 및 용량 검증
(2) 사진 메타데이터 분석 및 그룹화 페이지
- User Story
- “나는 업로드한 사진들의 촬영 시각과 위치 정보를 바탕으로, 같은 시간대와 장소에서 찍은 사진들을 자동으로 묶어보고 싶다. 필요하다면 내가 직접 그룹을 나누거나 합칠 수도 있어야 한다.”
- 목적: 시간/위치 기반 자동 그룹화와, 사용자의 수동 수정 기능
- Acceptance Criteria
- [AC2.1] 그룹화 알고리즘 적용
- 업로드 완료 시점에 클라이언트에서 시간·위치 기반 알고리즘(예: ‘2시간 이내 & 5km 이내’)을 사용해 자동 클러스터링을 수행한다.
- 그룹화 결과를 UI(카드 혹은 목록)로 사용자에게 즉시 보여준다 (예: ‘그룹1(파리 10:00-12:00), 그룹2(에펠탑 12:30-13:00)’ 등).
- [AC2.2] 그룹 분할/병합
- 자동 그룹화 결과가 부정확할 경우, 사용자는 그룹을 분할하거나 병합할 수 있어야 한다.
- 그룹 분할 시, 해당 그룹 내 사진들을 분할 기준(시간대/장소)을 지정해 2개 이상의 그룹으로 나눈다.
- 병합 시, 인접 그룹(또는 사용자가 선택한 두 그룹)을 하나로 합쳐 새 그룹을 만든다.
- [AC2.3] 인터랙션 및 미리보기
- 각 그룹을 클릭하면 해당 그룹에 속한 사진 썸네일 목록을 자세히 확인할 수 있다.
- 그룹화가 완료되면 “다음 단계로 이동(그룹 정보 입력)” 버튼이 나타난다.
- [AC2.1] 그룹화 알고리즘 적용
(3) 그룹 정보 입력 페이지
- User Story
- “나는 자동으로 생성된 사진 그룹을 확인하고, 각 그룹마다 방문지 이름과 평가(간단 리뷰, 별점 등)를 입력해 여행 기록에 의미를 부여하고 싶다.”
- 목적: 사용자 개별 입력(장소명, 후기, 별점 등)을 통해 그룹을 ‘의미 있는 방문지 단위’로 확정
- Acceptance Criteria
- [AC3.1] 그룹 리스트 표시
- 그룹화된 결과 목록(그룹 이름, 대표 사진, 촬영 위치/시간 요약)을 사용자에게 표시한다.
- 사용자는 각 그룹을 클릭하거나 펼쳐서 방문지 이름, 평가 등을 기입할 폼을 확인할 수 있다.
- [AC3.2] 방문지명/평가 입력
- 각 그룹에 방문지 이름(예: “에펠탑”)과 평가(텍스트 후기, 별점 등)를 입력할 수 있는 필드를 제공한다.
- 최소한의 필수 입력(방문지명 등)이 누락될 경우 경고 표시를 하고, “AI 요약 생성” 버튼을 비활성화한다.
- [AC3.3] 위치 자동 변환(선택사항)
- (옵션) 그룹의 GPS 좌표를 역지오코딩하여 “~동/파리 에펠탑 근처” 등의 추천 장소 이름을 미리 채워준다.
- 사용자는 자동 제안값을 수동으로 수정 가능하다.
- [AC3.4] 데이터 저장 & 다음 단계
- 모든 그룹에 대한 입력을 마친 후, “AI 여행기 생성” 버튼을 누르면, 이 정보를 임시 저장하고 AI 생성 페이지로 넘어간다.
- [AC3.1] 그룹 리스트 표시
(4) 여행 기록 생성 (AI 요약) 페이지
- User Story
- “나는 시간 순으로 정리된 그룹 정보(방문지명, 평가)를 AI가 종합해서 여행기를 자동으로 써주길 원한다. 이 과정을 간단한 조작만으로 해결하고 싶다.”
- 목적: AI(Large Language Model)를 활용하여 여행기 텍스트 초안을 생성
- Acceptance Criteria
- [AC4.1] 요약 생성 요청
- “AI 여행기 생성” 버튼을 클릭하면 서버(백엔드)로 여행 그룹 정보(일정, 평가, 날짜, 장소명 등)를 전송한다.
- 전송 시 로딩 인디케이터(“AI가 여행기를 작성 중입니다…”)를 표시한다.
- [AC4.2] LLM API 호출 및 응답 처리
- 백엔드에서 OpenAI API(GPT 등)를 통해 서술형 문장을 생성한 뒤, 성공 시 200 응답으로 여행기 텍스트를 반환한다.
- 요청에 실패할 경우 에러 메시지를 표시하고 재시도 버튼을 제공한다.
- [AC4.3] 여행기 초안 표시
- AI가 생성한 여행기 텍스트 초안을 사용자의 브라우저에 표시하고, 사용자는 이 초안을 확인/다음 단계로 넘어갈 수 있다.
- ‘수정/편집’ 페이지로 이동하기 전이라도 간단히 문단을 확인할 수 있어야 한다.
- [AC4.1] 요약 생성 요청
(5) 여행기 확인 및 편집 페이지
- User Story
- “나는 AI가 생성한 여행기 초안을 보고, 잘못된 부분을 수정하거나, 좀 더 자세한 내용을 추가해 최종 여행기로 확정하고 싶다.”
- 목적: AI가 작성한 텍스트를 사용자가 직접 편집하는 최종 과정
- Acceptance Criteria
- [AC5.1] AI가 생성한 본문 로드
- 이전 단계에서 생성된 텍스트를 텍스트 에디터 영역에 불러온다.
- 일부 문단이 분리되어 보이도록 기본 서식(날짜별 헤더 등)을 적용한다.
- [AC5.2] 사용자 편집 기능
- 에디터에서 글자 수정, 문단 추가/삭제, 스타일 변경(볼드, 이탤릭 등) 등을 할 수 있다.
- 사진 혹은 그룹별 대표 이미지 등을 본문에 삽입할 수도 있다(옵션).
- [AC5.3] 내용 저장
- “저장하기” 버튼을 누르면 수정된 텍스트 및 편집 상태가 DB(혹은 임시 스토리지)에 반영된다.
- 저장 완료 후 “SNS에 공유하기” 버튼 활성화 및 이동 가능.
- [AC5.1] AI가 생성한 본문 로드
(6) 여행기 SNS 공유
- User Story
- “나는 완성된 여행기를 내 SNS 계정(페이스북, 트위터 등)으로 쉽게 공유하고 싶다. 다른 사람도 링크 클릭만 하면 내 여행기를 볼 수 있어야 한다.”
- 목적: 공개 URL 생성 및 SNS에 원클릭 공유
- Acceptance Criteria
- [AC6.1] 공유용 URL 생성
- “SNS에 공유하기” 버튼 클릭 시 서버로 최종 여행기 데이터를 전송 → 고유 ID(슬러그 등) 기반으로 공유용 URL을 발급.
- 예: https://travelapp.com/share/abc123.
- [AC6.2] 웹 공유 API 활용
- 모바일 환경에서 navigator.share()(Web Share API)를 지원하면, 시스템 공유 다이얼로그가 뜨도록 한다.
- 미지원 브라우저에서는 트위터/페이스북/카카오톡 등 각 SNS에 직접 연결되는 링크를 제공한다.
- [AC6.3] 오픈 그래프 메타태그
- 공유용 페이지에 접근했을 때, 대표 이미지를 썸네일로 쓰고, 여행기 제목, 간단 요약이 미리보기 카드에 뜨도록 OG 태그를 설정한다.
- 공유가 성공하면 “공유되었습니다” 등의 확인 메시지를 표시한다.
- [AC6.1] 공유용 URL 생성
(7) 공유된 페이지(읽기 전용) 열람
- User Story
- “다른 사람이 SNS에서 공유된 링크를 클릭했을 때, 내 여행 기록 페이지를 보고 사진과 텍스트를 편하게 읽을 수 있으면 좋겠다.”
- 목적: 공유된 여행 기록을 웹 페이지 형태로 누구나 열람 가능
- Acceptance Criteria
- [AC7.1] 공개 페이지 렌더링
- URL /share/:id 형태로 접근 시, 해당 ID에 매핑된 여행기 본문과 사진을 SSR(서버사이드 렌더링) 또는 SSG(정적 페이지)로 표시한다.
- 여행기의 모든 문단, 날짜별 구분, 첨부 사진 등을 순서대로 볼 수 있어야 한다.
- [AC7.2] 반응형 디자인
- PC·모바일·태블릿 등 다양한 화면 크기에서 가독성 있게 페이지가 레이아웃을 조정해야 한다.
- [AC7.3] 작성자만 편집 가능
- 공유 페이지는 읽기 전용으로, 일반 방문자는 편집 버튼을 볼 수 없다.
- 작성자가 로그인 상태로 접근하는 경우에만 “수정하기” 버튼이 노출된다(옵션 기능).
- [AC7.1] 공개 페이지 렌더링
2. 각 인수조건을 위한 정책(Policy)과 알고리즘(Algorithm)
아래는 중요하거나 구현 난이도가 있는 인수 조건을 예시로 들어, 어떻게 처리해야 할지 정책과 알고리즘을 간략히 기술한 것입니다. (실제로는 모든 AC에 대해 세분화할 수도 있습니다.)
2.1 사진 업로드 (AC1 계열)
- AC1.1 (파일 형식 및 용량 검증)
- 정책(Policy)
- 지원 파일 형식: [".jpg", ".jpeg", ".png", ".heic"] 등 (디자인 결정에 따라 확장 가능)
- 최대 용량: 10MB(또는 5MB 등). 초과 시 즉시 거부.
- Drag&Drop, File input 양쪽에서 동일 로직 적용.
- 알고리즘(Algorithm) (의사코드 예시)
- 정책(Policy)
function handleFileUpload(files):
allowedExtensions = [".jpg", ".jpeg", ".png", ".heic"]
maxSize = 10 * 1024 * 1024 // 10MB
for file in files:
ext = getFileExtension(file)
if ext not in allowedExtensions:
showError("지원하지 않는 파일 형식입니다.")
continue
if file.size > maxSize:
showError("파일이 너무 큽니다. (최대 10MB)")
continue
readEXIF(file) // 다음 단계인 EXIF 추출 진행
- AC1.2 (EXIF 메타데이터 추출)
- 정책(Policy)
- 클라이언트에서 exif-js 또는 ExifReader 라이브러리를 사용해 EXIF 정보를 파싱.
- 만약 EXIF가 없거나 GPS/촬영일시가 누락된 경우, 후속 단계에서 "메타데이터 없음" 상태로 처리.
- 알고리즘(Algorithm) (의사코드 예시)
- 정책(Policy)
function readEXIF(file):
ExifReader.load(file).then(metadata => {
if metadata has GPS info:
location = extractCoordinates(metadata.GPS)
else:
location = null
if metadata has DateTimeOriginal:
timeStamp = parseDate(metadata.DateTimeOriginal)
else:
timeStamp = file.lastModifiedDate or null
storeInState({
file: file,
location: location,
timeStamp: timeStamp
})
}).catch(error => {
showError("EXIF 추출 중 오류 발생")
})
2.2 메타데이터 분석 및 그룹화 (AC2 계열)
- AC2.1 (시간·위치 기반 그룹화 알고리즘)
- 정책(Policy)
- 일정 기준(예: 시간차 2시간 이내, 거리 5km 이내)를 만족하는 사진들을 하나의 그룹으로 묶는다.
- 제안된 임계값은 사용자 입맛에 맞게 설정/조절 가능.
- 하루를 넘어가면 자동으로 날짜가 바뀌었다고 판단(날짜 구분).
- 알고리즘(Algorithm) (단순 예시: Greedy 방식)
- 정책(Policy)
sort photos by timeStamp ascending
set currentGroup = []
set groups = []
for photo in photos:
if currentGroup is empty:
currentGroup.add(photo)
else:
lastPhoto = currentGroup.last()
if timeDistance(photo, lastPhoto) <= 2 hours
AND locationDistance(photo, lastPhoto) <= 5 km:
currentGroup.add(photo)
else:
groups.add(currentGroup)
currentGroup = [photo]
// 마지막 그룹 추가
if currentGroup not empty:
groups.add(currentGroup)
return groups
2.3 그룹 정보 입력 (AC3 계열)
- AC3.2 (방문지명/평가 입력)
- 정책(Policy)
- 방문지명은 필수. 빈 칸이면 AI 요약 단계로 넘어가지 못한다.
- 평가(리뷰 텍스트, 별점 등)는 옵션.
- (옵션) 욕설/반사회적 단어 필터가 필요하다면 사전에 단어 리스트로 필터링.
- 알고리즘(Algorithm) (간단 검증 예시)
- 정책(Policy)
function validateGroupInputs(group):
if group.placeName is empty:
return "방문지명을 입력해야 합니다."
if containsProfanity(group.reviewText):
return "부적절한 표현이 포함되어 있습니다."
return "OK"
2.4 AI 여행 기록 생성 (AC4 계열)
- AC4.2 (LLM API 호출 및 응답 처리)
- 정책(Policy)
- OpenAI GPT-3.5/4 API 등 호출 시, 사용자 여행정보(장소명, 날짜, 평가)를 프롬프트에 포함하여 자연스러운 서술형 텍스트를 요청.
- 토큰 비용 관리: 여행 그룹이 너무 많으면 요약할 수 있도록 프롬프트를 최적화한다.
- 알고리즘(Algorithm) (백엔드 Pseudo-code)
- 정책(Policy)
function generateTravelStory(groupData):
prompt = buildPrompt(groupData) // "다음 방문지 목록을 참고하여 한국어 여행기를 작성..."
response = openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: [{role: "user", content: prompt}],
max_tokens: 1500,
temperature: 0.7
})
return response.data.choices[0].message.content
2.5 여행기 확인 및 편집 (AC5 계열)
- AC5.2 (사용자 편집 기능)
- 정책(Policy)
- 최소한 단순 텍스트 수정 가능. 필요한 경우 WYSIWYG 편집기를 붙인다.
- 자동 저장 또는 저장 버튼을 두고, 저장 시점에만 DB 반영.
- 알고리즘(Algorithm)
- 에디터 자체는 보통 서드파티 라이브러리(예: Quill, TipTap) 사용. 별도 알고리즘보다는 저장 로직이 중요.
- 정책(Policy)
function saveEditedStory(content):
dbUpdate(userId, travelId, { storyText: content })
showMessage("저장 완료")
2.6 SNS 공유 (AC6 계열)
- AC6.1 (공유용 URL 생성)
- 정책(Policy)
- 최종 여행 데이터를 “공유 허용” 상태로 DB에 저장하면, /share/:uniqueId 형태의 경로를 생성.
- uniqueId는 UUID나 해시.
- 공유 시 접속 권한을 ‘공개’로 설정.
- 알고리즘(Algorithm)
- 정책(Policy)
function createShareLink(travelId):
uniqueId = generateUUID()
dbUpdate(travelId, { shareId: uniqueId, isPublic: true })
return "https://domain.com/share/" + uniqueId
2.7 공유 페이지 열람 (AC7 계열)
- AC7.1 (공개 페이지 렌더링)
- 정책(Policy)
- SSR(Next.js의 getServerSideProps 등) 사용 시, 요청된 shareId를 DB에서 조회해 여행기 정보를 불러온다.
- isPublic이 true인 경우에만 페이지 렌더링. 아니면 “존재하지 않는 페이지” 안내.
- 알고리즘(Algorithm) (SSR 예시)
- 정책(Policy)
function getServerSideProps(context):
shareId = context.params.shareId
travelData = dbSelectByShareId(shareId)
if (!travelData || !travelData.isPublic):
return { notFound: true } // 404 페이지
return {
props: {
story: travelData.storyText,
photos: travelData.photoList,
...
}
}
이 기획 문서를 기반으로 하여 각 인수 조건을 달성하는 형태의 개발을 진행할 예정입니다.
다음 글에서는 기술적인 부분을 정리하고 문서화하는 내용을 담아보겠습니다.
'AI로 1인 개발' 카테고리의 다른 글
| AI로 앱 만들기 #4 - 백엔드 설계하기 (1) | 2025.04.21 |
|---|---|
| AI로 앱 만들기 #3 - DB 설계하기 (1) | 2025.04.14 |
| AI로 앱 만들기 #2 - ChatGPT 프로젝트, 개발 환경 설정하기 (0) | 2025.04.08 |