frindlymap/README.md

7.7 KiB
Raw Blame History

Friendly Map

가족·친구·직장 관계를 한 앱에서. 새 친구가 "어디서 봤지?" 싶을 때 목적별 명함을 주고받아 관계를 유추한다. 안드로이드 우선, Flutter. 모든 기기가 서버 겸 클라이언트(P2P)이고, 외부 DB는 암호화된 임시 우편함일 뿐이다.

원칙 (1순위)

  1. 구조가 기능보다 먼저다. 화면과 내부를 분리한다.
  2. 모듈은 언제든 빼고 끼울 수 있다. 기능을 더하거나 빼도 다른 모듈을 고치지 않는다.
  3. 파일은 300~400줄, 최대 700줄.
  4. 모듈별로 시험한다. 시험이 끝난 모듈은 다시 만들지 않고 그 위에 쌓는다.
  5. 한 번에 한 플랫폼. 지금은 안드로이드. 다른 플랫폼은 platform_<이름> 모듈만 새로 붙인다.

모듈과 의존 방향 (tool/check_modules.py 가 검사)

모듈 하는 일 쓸 수 있는 모듈
core 모듈끼리 주고받는 약속(키·전송·암호·봉투·명함 항목 이름) 없음
platform 기기마다 다른 기능의 약속(보안 저장소, 저장 공간, 센서, QR, 발견, 위치, 통화 기록·전화, 알림) 없음
platform_android platform 의 안드로이드 구현(플러그인은 여기서만) core, platform, storage
storage 저장소 약속 + 메모리/SQLite 구현 platform
keys 12단어 복구 코드, 키 파생, 둘만 아는 키 암호, 복구 조각 나누기·합치기 core, platform
sharing 명함·대화방이 누구와 공유되는지 storage
sync 봉투 만들기·반영, 충돌 규칙, 시계 맞추기, 재전송 core, storage
transport 메모리·직접 연결·외부 보관함·늦게 만드는 연결 core
discovery QR 내용, 흔들기 감지, 같은 네트워크 주소록 platform
inference "어디서 봤지?" 유추와 추천 core
visibility 공개 범위 표(정보 × 관계)와 명함 거르기. 순수 계산 core
layout 홈·명함 틀(템플릿) 순서/보이기/편집 core
kinship 가계도로 촌수·호칭 계산(순수) 없음
events 생일·기일 일정, 알림 날짜 계획, 통화 기록 요약(순수) 없음
app 서비스 묶음(FriendlyApp). 화면이 쓰는 유일한 내부 입구 위 모듈들(키·전송 구현 제외)
ui 화면. app 의 서비스만 쓴다 app, core, platform, storage, discovery, inference, layout, visibility, kinship, events

모듈 폴더 밖의 lib/main.dart, lib/composition/ 이 부품을 고르는 유일한 곳이다. 암호 구현·저장소·연결 방식을 바꾸려면 lib/composition/android_bootstrap.dart 의 해당 줄만 바꾼다.

시험

tool/test_module.sh            # 모듈 목록
tool/test_module.sh sync       # 모듈 하나만
flutter test                   # 전체
python3 tool/check_modules.py  # 줄 수·import 방향

시작

flutter create --platforms=android --org com.friendlymap --project-name friendly_map .
python3 tool/android_patch.py      # 권한, 평문 통신, minSdk 23
flutter pub get
flutter analyze
flutter test
flutter run

보관함 서버(선택): server/README.md. 서버 주소를 설정 mailbox_url 에 넣으면 같은 네트워크 밖에서도 주고받는다.

알려진 한계

  • 보관함의 우편함 토큰은 번호별 처음 쓴 기기에 묶인다(번호 선점 가능). 나중에 문자 인증으로 막는다.
  • 그룹 대화방은 사람이 나가도 키를 바꾸지 않는다.
  • 위치는 지도 타일 없이 레이더(내 위치 중심, 방위·거리)로 보여 준다. 지도 SDK는 붙이지 않았다.
  • 문자 인증은 만들어 뒀지만 문자 발송 주소(--sms-webhook)를 정해 서버에 넣어야 켜진다. 켜기 전에는 처음 쓴 기기에 묶이는 방식(번호 선점 가능)이다.

가족 모임 (촌수 · 통화 요약 · 생일/기일 알림)

  • 홈의 "가족 모임"에서 가족을 한 분씩 추가한다. 번호가 없는 친척도 넣을 수 있다(이 기기 안에서만 쓰이고 친구 요청은 가지 않는다).
  • "나의 부모/자녀/배우자"를 고르거나 부모·자녀·배우자를 이어 가계도를 만들면, 나와 내 자녀 각각의 눈으로 본 호칭과 촌수가 나온다(예: 내게는 친사촌 형 4촌, 내 아들에게는 당숙 5촌).
  • 사람을 누르면 관계, 통화 횟수·시간·마지막 통화·통화 잘 되는 시간대, 주제 메모, 다가오는 생일/기일을 요약하고 전화·문자·대화 버튼이 있다.
  • 돌아가신 날을 적으면 맨 위에 "故" 띠가 뜨고(설정에서 끌 수 있다) 생일 대신 기일을 알린다.
  • 설정 > "생일·기일 알림"에 7, 2, 1, 0 처럼 적으면 그 날짜 전 오전 9시에 알림이 울린다(기본 1일 전·당일).
  • 음력: 생일·기일마다 "음력"을 켤 수 있다(1951~2049년 변환표 내장, 기일은 윤달도 지원). 제사 알림이 음력 날짜로 잡힌다.
  • 한계: 통화 "내용"은 기기에 남지 않으므로 주제는 직접 적는다. 통화 기록 읽기(READ_CALL_LOG)는 Play 스토어 심사 제한이 있다.
  • 안드로이드: call_log, url_launcher, flutter_local_notifications, timezone 플러그인과 권한·수신기·desugaring 설정이 필요하다(tool/android_patch.py 가 맞춘다).

복구 조각 (친구에게 맡기기)

  • 설정 → 친구에게 맡기기: 12단어를 n조각으로 나눠(필요한 수 k) 친구마다 한 장씩 암호화해 보낸다. 친구 한 명의 조각으로는 아무것도 알 수 없다.
  • 친구는 설정 → 내가 맡은 조각에서 조각 글자를 본다. 새 폰에서 복구할 때 필요한 수만큼의 조각 글자를 "복구 코드로 되살리기" 칸에 한 줄씩 붙여 넣는다.
  • 조각 수나 필요한 수를 바꾸면 새로 나눠야 하고, 이전에 맡긴 조각은 새 조각과 섞이지 않는다.

공개 범위

  • 내 정보 → 공개 범위 표(학교 · 회사·직책 · 생일 · 연락처 × 가족 · 회사 · 동문 · 그룹)에서 칸을 눌러 공개/일부/숨김을 정한다.
  • 명함을 보내거나 "다시 보내기"를 할 때 받는 사람의 관계(연결된 관계 + 보내는 명함 종류)에 맞게 걸러서 보낸다. 관계를 모르는 사람에게는 내가 고른 그대로 간다.
  • 표를 고쳐도 이미 보낸 명함은 "다시 보내기" 전까지 그대로다.

위치 공유

  • 홈의 "위치" 구역에서 켠다. 기본은 꺼짐.
  • 켜면 1분마다, 공개 범위 표의 "실시간 위치"가 공개·일부인 관계의 친구에게만 보낸다. 관계를 모르는 친구에게는 보내지 않는다. "일부"는 약 1km 단위로 뭉개서 보낸다.
  • 위치는 둘만 아는 키로 잠가서 보내고, 받은 쪽에는 마지막 위치 하나만 남는다.
  • 안드로이드: geolocator 플러그인과 위치 권한이 필요하다 (tool/android_patch.py 가 권한을 넣는다).

번호 확인 (문자 인증)

  • 내 정보 → "내 번호 확인하기": 문자로 온 6자리를 넣으면 이 기기의 토큰이 내 번호에 묶인다. 폰을 바꾼 진짜 주인은 다시 확인하면 새 기기로 넘어오고, 이전 기기는 막힌다.
  • 서버: python3 mailbox_server.py --sms-webhook <주소> --require-verify. 문자 발송은 SmsSender 하나(웹훅은 {to,text} 를 POST)라 업체를 바꿔도 서버 나머지는 그대로다.
  • --require-verify 를 켜면 확인을 마친 기기만 우편함을 열 수 있다 (번호 선점 차단). 켜지 않으면 예전처럼 처음 쓴 기기에 묶인다.
  • 5분 만료, 번호당 1시간 3번, 5번 오답이면 새로 요청. --sms-log 는 시험용(문자를 보내지 않고 기록)이라 실서비스에서 쓰면 안 된다.
  • 서버 시험: cd server && python3 -m unittest test_mailbox test_verify