1.0x
> cd ..

이 글은 AI로 프로젝트를 분석해서 쓴 글입니다.

pic·blog

pic·blog

2026-08-26 ~ 진행 중

Intro

pic·blog는 사진과 지도, 짧은 이야기를 함께 남기는 개인 여행 기록입니다. 사진의 EXIF에 담긴 GPS 좌표와 촬영 시각을 이용해 방문 장소와 동선을 만들고, 여기에 장소 이름과 글을 붙일 수 있습니다.

여행 사진을 시간순으로 넘겨보면 무엇을 봤는지는 알 수 있지만, 어디에서 찍었고 어떤 순서로 돌아다녔는지는 따로 떠올려야 합니다. pic·blog는 사진에 이미 들어 있는 정보를 이용해 여행을 다시 볼 수 있도록 구성했습니다.

글을 길게 쓰기 전에 사진을 먼저 올려 기록의 뼈대를 만들고, 나중에 이야기를 더하는 방식입니다. 보는 사람은 공개 링크로 바로 들어오고, 편집만 비밀번호로 보호합니다. 여러 사용자의 계정을 관리하는 서비스보다, 제가 여행을 기록하고 공유하는 데 필요한 범위에 집중했습니다.

사이트 링크

상단과 본문의 화면은 실제 운영 중인 공개 여행 기록에서 촬영했습니다. 모바일 화면은 웹의 반응형 화면입니다.

주요 기능

사진에서 방문 장소 만들기

브라우저에서 사진의 위치와 촬영 시각을 읽고, 거리와 촬영 순서, 일차와 시간 공백을 함께 고려해 포인트를 만듭니다. 같은 곳에서 찍었더라도 다른 날이거나 긴 시간이 지난 뒤 다시 방문한 경우를 구분합니다. 포인트마다 이름과 태그, 본문을 붙여 여행의 한 장면으로 정리할 수 있습니다.

한 번 정해진 포인트 좌표는 사진을 추가했다고 자동으로 움직이지 않습니다. 다시 묶고 싶을 때는 편집 화면에서 반경을 바꾸도록 했고, 이 과정에서 기존 설명이 사라지는 경우에는 대상을 먼저 보여주고 확인을 받습니다.

촬영 순서로 이어지는 지도

지도에서는 사진을 찍은 순서대로 좌표를 이어 동선을 보여줍니다. 장소별 대표 좌표만 연결하면 같은 장소 안에서 움직인 기록이 사라지기 때문에, 경로와 거리 계산에는 개별 사진의 좌표를 사용합니다.

촬영 시각을 모르는 사진은 경로에서 제외합니다. 표시하는 거리도 사진 사이의 좌표를 이은 값으로, 도로를 따라 측정한 이동 거리나 GPS 연속 기록과는 차이가 있습니다. 사진으로 남은 여행의 흐름을 보는 용도입니다.

시즈오카·이즈 여행의 날짜별 사진 포인트와 촬영 순서 동선

모바일에서 기록하고 링크로 공유하기

휴대폰에서 사진을 선택하고 장소별 내용을 편집할 수 있습니다. 방문자는 로그인 없이 사진과 지도를 함께 둘러볼 수 있고, PWA로 홈 화면에 추가해 사용하는 것도 가능합니다.

화면은 사진이 먼저 보이도록 구성하고, 지도와 이야기가 그 옆에서 맥락을 보태도록 했습니다. 사진을 올리는 과정과 여행을 감상하는 과정이 작은 화면에서도 이어지는 데 신경 썼습니다.

방문 장소를 선택하면 사진과 이야기가 지도와 함께 열리는 화면 휴대폰 너비에서 보는 장소 설명과 여행 사진

AI로 사진 정리와 기록 보강하기

최근에는 사진과 메타데이터를 분석하는 Claude 기반 기능을 더했습니다. 사진을 다시 묶고 제목을 제안하는 정리 모드와, 기존 포인트를 유지하면서 대표 사진·짧은 설명·장소명 후보를 제안하는 보강 모드가 있습니다. 위 장소 상세 화면의 설명도 실제 보강 결과입니다.

제안은 검토한 뒤 적용합니다. 장소명은 근거 사진과 함께 후보로 남기고 기존 제목을 자동으로 덮어쓰지 않습니다. 사용자가 쓴 글이나 사진의 소속을 보호하고, 검토하는 동안 원본이 바뀌었다면 적용을 막도록 했습니다. 적용 당시의 상태가 유지되는 경우에는 되돌릴 수도 있습니다.

사진이 많을 때는 최대 1,000장을 여러 요청으로 나눠 비동기로 분석합니다. AI가 반환한 사진 ID와 포인트 ID를 서버에서 검사하고, 사진 누락이나 중복을 확인하는 방식입니다.

MCP도 연결해 외부 AI 클라이언트에서 여행 기록을 읽고 제한된 항목을 수정할 수 있습니다. 웹 편집 비밀번호와는 별도로 OAuth 승인을 거치는 경로입니다.

기술 스택

영역사용 기술
프레임워크Nuxt 4, Vue, TypeScript
UIReka UI, CSS 토큰, scoped CSS
지도Mapbox GL JS
사진 메타데이터exifr
iPhone 앱Flutter WebView, PhotoKit 연동
저장SQLite, better-sqlite3, 로컬 파일
인증nuxt-auth-utils, 비밀번호 기반 편집 권한
사진 AI·외부 연결Anthropic Claude, MCP, OAuth
검증Vitest, Playwright
배포Docker, GitHub Actions, Cloudflare Tunnel

구현하면서 신경 쓴 부분

이미지 처리는 브라우저에서

웹에서는 사진을 브라우저에서 리사이즈한 뒤 서버로 전송합니다. 긴 변 기준 최대 2,048px의 감상용 이미지와 400px의 썸네일을 만들고, 서버에는 원본을 따로 보관하지 않습니다.

여러 장을 올릴 때도 한 장씩 처리하고 메모리를 해제하는 흐름으로 구성했습니다. 서버가 이미지 변환까지 맡지 않아도 되므로, 사진을 받으면 파일로 저장하는 단순한 구조를 유지할 수 있습니다.

iPhone용으로는 Flutter WebView 앱도 만들었습니다. iOS의 웹 사진 선택 과정에서 큰 이미지를 변환하느라 기다리는 문제를 줄이기 위해 PhotoKit과 연결했습니다. 메타데이터는 필요한 부분만 읽고, 이미지도 필요한 크기로 받아 웹의 업로드 흐름에 넘깁니다. 사진을 묶는 규칙과 편집 로직은 웹과 공유합니다.

작은 서버에서 관리하기 쉬운 구조

데이터는 SQLite 파일에, 사진은 서버의 폴더에 저장합니다. 외부 DB나 오브젝트 스토리지를 따로 두지 않고 앱 프로세스와 데이터 폴더를 관리하는 구성입니다.

개인 여행 기록이라는 규모에 맞춰 운영할 구성 요소를 줄였습니다. 대신 컨테이너를 교체해도 DB와 사진이 유지되도록 데이터 디렉터리를 분리해 두었습니다.

pic·blog는 사진을 골라 올리는 것부터 기록을 시작할 수 있는 프로젝트입니다. 지도 위에서 당시의 사진을 다시 찾아보고, 필요한 이야기를 더해 여행을 정리하는 데 초점을 맞췄습니다.