[MOBILE-012][실무] 인터넷 없이도 쓰는 메모 앱 설계하기 > IT 기술 공유

본문 바로가기

사이트 내 전체검색

뒤로가기 IT 기술 공유

[MOBILE-012][실무] 인터넷 없이도 쓰는 메모 앱 설계하기

페이지 정보

작성자 기술팀장 작성일 26-09-06 20:36 조회 44 댓글 0

본문

[이번 수업]

인터넷이 끊겨도 메모를 쓰고 읽으며 연결이 돌아오면 동기화하는 앱을 설계합니다. 로컬 저장소, 작업 대기열, 재시도, 충돌, 화면 상태를 연결합니다.

[선수지식]

MOBILE-004의 상태와 생명주기, MOBILE-005의 API 오류 처리, MOBILE-006의 로컬 저장·캐시, MOBILE-010의 네트워크 성능을 알면 좋습니다.

[학습목표]

1. 오프라인에서도 핵심 읽기·쓰기를 유지한다.
2. 중복 없이 재시도하는 동기화 대기열을 설계한다.
3. 대기·실패·충돌 상태를 사용자가 이해하게 표시한다.

[핵심개념]

오프라인 우선은 네트워크가 없어도 로컬 데이터를 기준으로 핵심 기능을 제공하는 설계입니다. 사용자가 저장하면 먼저 로컬 데이터베이스에 반영하고 전송할 작업을 아웃박스에 넣습니다. 아웃박스는 아직 서버로 보내지 못한 변경을 보관하는 대기열입니다. 화면은 서버 응답을 기다리지 않고 로컬 결과를 보여 줍니다.

각 작업에는 고유한 연산 ID를 붙입니다. 같은 요청이 재전송돼도 서버가 한 번만 반영하도록 멱등성을 보장해야 앱 종료, 시간 초과, 연결 전환에서 중복 생성이 생기지 않습니다. 동기화 상태는 pending, syncing, synced, failed, conflict처럼 나눕니다. 단순한 온라인 여부 알림만 믿지 말고 실제 요청 결과로 성공을 판단합니다.

재시도는 즉시 무한 반복하지 않습니다. 일시적 오류에는 간격을 점점 늘리고 무작위 지연을 더하며, 인증 실패나 잘못된 입력은 자동 재시도하지 않습니다. 충돌은 로컬과 서버가 같은 항목을 서로 다르게 바꾼 상태입니다. 수정 버전과 시각을 비교하고, 자동 병합이 안전하지 않으면 두 내용을 보여 주어 사용자가 선택하게 합니다.

[따라하기]

offline_queue.py를 저장하세요. 실제 서버 없이 상태 전환만 확인하는 로컬 예제입니다.
```python
notes = {
    "n1": {"text": "오프라인 메모", "state": "pending"}
}
outbox = [
    {"operation_id": "op-001", "note_id": "n1", "text": "오프라인 메모"}
]
server = {}
applied = set()

def sync(online):
    if not online:
        return f"오프라인: {len(outbox)}건 대기"
    sent = 0
    for operation in outbox[:]:
        key = operation["operation_id"]
        if key not in applied:
            server[operation["note_id"]] = operation["text"]
            applied.add(key)
            sent += 1
        notes[operation["note_id"]]["state"] = "synced"
        outbox.remove(operation)
    return f"동기화 완료: {sent}건"

print(sync(False))
print(sync(True))
print(f"로컬={notes['n1']['state']} 서버={server['n1']}")
```

macOS·Linux는 `python3 offline_queue.py`, Windows는 `py offline_queue.py`를 실행합니다. 예상 결과는 `오프라인: 1건 대기`, `동기화 완료: 1건`, `로컬=synced 서버=오프라인 메모`입니다. 이 코드는 원리를 보여 주며 실제 앱에서는 아웃박스와 적용된 연산 ID를 영구 저장해야 앱이 종료돼도 남습니다.

화면에는 저장됨과 서버 동기화됨을 구분해 표시하세요. pending에는 “기기에 저장됨·전송 대기”, failed에는 재시도 버튼과 이유, conflict에는 로컬·서버 내용을 비교하는 선택 화면이 필요합니다. 앱을 강제 종료하거나 네트워크를 바꿔도 작성 내용이 사라지지 않아야 합니다.

[흔한 실수]

연결이 끊겼다는 이유로 저장 버튼을 막거나, 메모를 메모리에만 두지 마세요. 시간 초과를 실패로 보고 새 ID로 재전송하면 중복이 생깁니다. 모든 오류를 같은 간격으로 무한 재시도하거나 마지막 저장 시각만으로 내용을 덮어쓰는 것도 위험합니다.

[보안 주의]

로컬 저장소에도 개인정보와 인증 토큰을 평문으로 남기지 않습니다. 운영체제가 제공하는 보호 저장소를 사용하고 로그·알림 미리보기에는 민감한 내용을 가립니다. 사용자별 데이터와 동기화 대기열을 분리하고 로그아웃 때 처리 정책을 정하세요. 인증서 검증을 끄지 말고 본인 소유의 로컬·격리 환경과 가짜 데이터로만 실습합니다.

[직접 해볼 과제]

같은 operation_id가 두 번 들어와도 서버에는 한 번만 반영되는 테스트를 추가하세요. 이어 서버 버전 2와 로컬 버전 2가 서로 다른 내용을 가진 충돌을 만들고, 자동 덮어쓰기 대신 두 값을 출력해 선택을 요구하도록 확장합니다.

[확인문제]

1. 저장을 서버 응답보다 먼저 로컬에 반영하는 이유는 무엇인가요?
2. 동기화 작업마다 고유한 연산 ID가 필요한 이유는 무엇인가요?
3. 인증 실패와 일시적 네트워크 오류의 재시도 정책이 달라야 하는 이유는 무엇인가요?

[다음 학습]

IOT-012에서 센서 측정부터 메시지 전송, 저장, 대시보드까지 전체 흐름을 완성합니다.

[공식 참고 자료]

https://developer.android.com/topic/architecture/data-layer/offline-first
https://developer.android.com/develop/background-work/background-tasks/persistent/getting-started
https://developer.apple.com/documentation/backgroundtasks
https://mas.owasp.org/MASVS/
https://www.rfc-editor.org/rfc/rfc9110.html

댓글목록 0

등록된 댓글이 없습니다.

Copyright © 소유하신 도메인. All rights reserved.

사이트 정보

회사명 : 회사명 / 대표 : 대표자명
주소 : OO도 OO시 OO구 OO동 123-45
사업자 등록번호 : 123-45-67890
전화 : 02-123-4567 팩스 : 02-123-4568
통신판매업신고번호 : 제 OO구 - 123호
개인정보관리책임자 : 정보책임자명

PC 버전으로 보기