전체 글 (137) 썸네일형 리스트형 6편: 사진을 지역 모양대로 잘라 지도에 그리기 색칠 대신 사진지역을 단색으로 칠하는 것까지는 금방 됐습니다. 그런데 색만 칠해 두니 "다녀왔다"는 사실만 남고 무엇을 봤는지가 안 남았습니다.그래서 사진이 있는 지역은 대표 사진(촬영순 첫 장)을 그 지역 모양대로 잘라서 그리기로 했습니다. 사진이 없는 방문 지역만 단색입니다.구현은 간단합니다.지역의 Path로 clipPath그 안에 drawImageRect로 사진을 그림경계선은 그 위에 그대로 덧그림지도를 확대하면 사진도 같이 커져야 하므로, 지역의 화면상 사각형(dst)에 맞춰 매 프레임 BoxFit.cover처럼 계산합니다.디코딩 파이프라인을 맞춰야 합니다사진을 매번 원본으로 디코딩할 수는 없어서, 320px로 줄여서 캐시합니다. 지역이 바뀌거나 사진이 바뀐 것만 다시 디코딩하고, 빠진 것은 di.. 5편: EXIF GPS로 지역 자동 판정, 안드로이드에서 막힌 지점 사진은 복사하고, DB에는 파일 이름만사진을 고르면 앱 문서 디렉터리 아래 photos/로 복사합니다. DB에는 파일 이름만 저장합니다.절대 경로를 저장하면 안 됩니다. iOS는 앱을 업데이트할 때 앱 디렉터리의 절대 경로가 바뀝니다. 경로를 저장해 두면 업데이트 후 모든 사진이 깨집니다. 읽을 때마다 문서 디렉터리를 구해서 파일 이름을 붙이는 게 맞습니다.목록에 사진을 띄울 때는 두 가지를 꼭 챙깁니다.cacheWidth 지정 — 원본 해상도로 디코딩하면 메모리가 순식간에 올라갑니다errorBuilder 지정 — 파일이 없을 때 앱이 죽지 않게EXIF 촬영일시native_exif로 촬영일시를 읽습니다. EXIF 날짜는 yyyy:MM:dd HH:mm:ss 형식이라 그대로 파싱하면 실패합니다. 날짜 부분의.. 4편: 공유를 염두에 둔 DB 설계, 그리고 구조를 갈아엎은 이야기 로컬 DB인데 동기화를 먼저 생각했습니다지금은 전부 기기 안에 저장합니다. sqflite를 쓰는 평범한 구조입니다. 그런데 나중에 공유 기능을 붙이면 서버와 동기화를 해야 합니다. 그때 가서 스키마를 바꾸면 이미 쓰고 있는 사용자 데이터를 건드려야 하니, 처음부터 세 가지를 넣어 뒀습니다.uuid — 로컬 id는 기기마다 다르니, 전역 식별자를 따로 둡니다updated_at — 어느 쪽이 최신인지 판단할 기준deleted_at — 소프트 삭제. 진짜 지우면 "삭제됐다"는 사실을 서버에 전달할 수 없습니다조회는 전부 deleted_at IS NULL 조건이 붙습니다. 여행을 삭제해도 하위 데이터를 연쇄 삭제하지 않고, 조회 쿼리에서 상위 여행의 삭제 여부까지 확인하는 방식으로 처리합니다. sqlSELECT.. 3편: CustomPainter로 지도 그리기, 그리고 900배 확대 그리는 구조구조는 단순합니다.CustomPainter가 시군구 250개의 Path를 그립니다그 위에 시도 경계선을 더 굵게 덧그립니다전체를 InteractiveViewer로 감싸서 핀치 줌과 이동을 처리합니다위경도를 화면 좌표로 바꾸는 투영은 직접 만들었습니다. 우리나라 정도 범위라면 복잡한 투영이 필요 없고, 경도에 cos(기준위도)를 곱해서 가로를 보정하는 정도면 충분합니다. 기준 위도는 35.9도로 잡았습니다. lonMin 124.6 / lonMax 131.0 / latMin 33.1 / latMax 38.7 / latRef 35.9scale = min(width / rawW, height / rawH)처음에 밟은 지뢰들선 굵기가 같이 커집니다. 확대하면 경계선도 같이 두꺼워져서 금방 지저분해집니다.. 2편: 시군구 250개 지도 데이터, 확대하면 틈이 벌어졌습니다 지도 SDK 대신 GeoJSON지도 SDK를 붙이면 타일 이미지가 깔립니다. 하지만 제가 원한 건 도로도 건물도 없는, 지역 모양만 있는 지도였습니다. 지역이 색으로 채워지는 게 전부인 화면이라 타일은 오히려 방해였습니다.그래서 공개 데이터를 썼습니다.출처: southkorea/southkorea-maps (통계청 2018 시군구 경계)시도 17개로 시작했다가 시군구 250개로 세분화했습니다. 시도 단위는 한 번 여행하면 금방 칠해져서 재미가 없습니다원본은 18MB라 저장소에는 넣지 않고 변환 결과만 커밋했습니다경계 데이터는 그대로 쓰기엔 너무 무겁습니다. 점 개수를 줄이는 단순화(simplify)가 필요합니다.그런데 확대하면 틈이 생겼습니다부산 중구와 부산진구 사이에 흰 선 같은 틈이 보였습니다. 처음.. 1편: 여행 기록을 지도에 채우는 앱, 기획부터 법규 검토까지 시작은 대동여지도였습니다여행을 다녀와도 사진은 갤러리에 묻히고, 어디를 갔었는지는 금방 흐려집니다. 그래서 다녀온 지역이 지도에 하나씩 채워지는 앱을 만들기로 했습니다. 지도를 채워 나간다는 감각이 핵심이고, 사진은 그 지역을 설명하는 재료입니다.기술 스택은 Flutter(안드로이드 + iOS)로 정했습니다. 지도는 구글 맵 같은 지도 SDK를 쓰지 않고, GeoJSON을 직접 그리는 방식을 택했습니다. 이유는 2편에서 자세히 다루겠습니다.제품 원칙을 먼저 못 박았습니다개발하면서 계속 흔들리는 지점이 생기기 때문에, 처음에 원칙을 정해 두었습니다.여행이 중심이다. 사용자가 여행(제목 + 기간)을 먼저 만들고, 그 안에 지역과 사진을 채운다사진은 여행 안에서만 추가한다. 지도에서 지역을 눌렀을 때 뜨는 시.. [쪽지 기능 2편] 운영자, 백그라운드 탭, 관리자 화면 — 만들면서 걸린 것들 1편에서는 쪽지를 누구에게 열어줄지, 게스트가 사라지면 대화를 어떻게 할지 같은 정책을 정리했습니다. 이번 편은 그걸 실제로 만들면서 걸린 세 가지입니다.운영자는 players에 없다쪽지 테이블의 from_key/to_key는 players 테이블을 외래 키로 참조합니다. 1편에서 말한 CASCADE 정리가 이 구조 덕분에 돌아가요.그런데 운영자 쪽지를 넣으려니 구멍이 하나 보였습니다. 운영자는 players에 행이 없어요. 관리자 대시보드(썬더박스)는 별도 인증을 쓰니까요. 운영자가 보내는 쪽지에는 넣을 보낸 사람 키가 없고, 사용자가 운영자에게 답장하면 넣을 받는 사람 키가 없습니다.운영자용 가짜 players 행을 하나 만드는 방법도 있었지만, 그러면 랭킹이나 친구 조회 같은 곳에서 그 행을 매번 .. [쪽지 기능 1편] 익명 서비스에서 쪽지를 누구에게 열어줄 것인가 PlayTalk에 쪽지 기능을 붙였습니다.그동안 PlayTalk에서 다른 사람과 이야기하는 방법은 둘뿐이었어요. 랜덤 매칭으로 만난 상대와 채팅하거나, 친구에게 대화 신청을 보내는 것. 둘 다 상대가 지금 접속해 있어야 합니다. 친구가 오프라인이면 할 수 있는 게 없고, 건의함에 의견을 남겨준 사용자에게 운영자인 제가 답할 방법도 없었어요.쪽지는 흔한 기능이라 금방 끝날 줄 알았는데, 구현 전에 검토하면서 정해야 할 게 열 개 넘게 나왔습니다. 그래서 두 편으로 나눠 정리합니다. 1편은 무엇을 허용할지 정한 이야기, 2편은 막상 만들면서 걸린 것들입니다.누구에게 보낼 수 있게 할 것인가처음 떠올린 발송 지점은 세 곳이었습니다. 친구 목록, 건의함 답변, 그리고 쪽지 화면에서의 답장이요. 여기에 신고 처리.. 이전 1 2 3 4 ··· 18 다음