오프라인 묵상 백엔드 (Sync API + 테스트)

ID: 66ece3fb-814f-4492-a1f5-d5b88766c038

보통 완료

## 목표
오프라인 저장된 묵상 데이터를 서버에 동기화하는 API 엔드포인트 + 테스트

## 구현 내용

### 1. Sync API 라우트 (`config/routes.rb`)
```ruby
namespace :qt do
resources :meditations, only: [:create, :update] do
member do
post :organize
end
collection do
post :sync # 새로 추가
end
end
end
```

### 2. Sync 액션 (`app/controllers/qt/meditations_controller.rb`)
기존 MeditationsController에 sync 액션 추가:
```ruby
def sync
results = []
sync_params[:meditations].each do |med_data|
content = current_user.qt_sessions_contents.find_by(id: med_data[:qt_content_id])
next unless content

meditation = current_user.user_meditations.find_or_initialize_by(
qt_content_id: med_data[:qt_content_id],
meditation_date: med_data[:meditation_date]
)

# 충돌 해결: 서버에 이미 데이터가 있고 더 최신이면 서버 우선
if meditation.persisted? && meditation.updated_at > Time.parse(med_data[:created_at])
results << { qt_content_id: med_data[:qt_content_id], status: "conflict", server_data: meditation_json(meditation) }
next
end

meditation.assign_attributes(
personal_meditation: med_data[:personal_meditation],
action_plan: med_data[:action_plan],
prayer_topic: med_data[:prayer_topic],
mood_after: med_data[:mood_after],
is_tongtok_completed: med_data[:is_tongtok_completed]
)

if meditation.save
results << { qt_content_id: med_data[:qt_content_id], status: "synced", id: meditation.id }
else
results << { qt_content_id: med_data[:qt_content_id], status: "error", errors: meditation.errors.full_messages }
end
end

render json: { results: results }
end
```

### 3. Strong Parameters
```ruby
def sync_params
params.permit(meditations: [:qt_content_id, :meditation_date, :personal_meditation, :action_plan, :prayer_topic, :mood_after, :is_tongtok_completed, :created_at])
end
```

### 4. 테스트 (`test/controllers/qt/meditations_controller_test.rb`)
기존 테스트 파일에 sync 테스트 추가:
- POST /qt/meditations/sync 인증 필요
- POST /qt/meditations/sync 새 묵상 생성 (synced)
- POST /qt/meditations/sync 기존 묵상 업데이트 (synced)
- POST /qt/meditations/sync 충돌 시 서버 우선 (conflict)
- POST /qt/meditations/sync 잘못된 qt_content_id 무시
- POST /qt/meditations/sync 빈 배열 처리
- POST /qt/meditations/sync 여러 건 배치 처리

### 5. qt_sessions_contents 헬퍼
sync에서 qt_content_id 유효성 검사를 위해 현재 사용자가 접근 가능한 콘텐츠 확인 필요.
기존 QtController에 이미 세션→콘텐츠 관계가 있으므로 참고:
- User → qt_participants → qt_sessions → qt_theme → qt_contents

## 파일 목록 (이 파일들만 수정/생성)
- `config/routes.rb` (수정 - sync 라우트 추가)
- `app/controllers/qt/meditations_controller.rb` (수정 - sync 액션 + sync_params 추가)
- `test/controllers/qt/meditations_controller_test.rb` (수정 - sync 테스트 추가)

## 주의사항
- UUID PK: ApplicationRecord의 set_uuid 패턴 사용
- SQLite: parallelize(workers: 1) 필수
- 기존 create/update/organize 액션 절대 수정 금지
- 기존 테스트 전체 통과 확인 필수 (bin/rails test)
- JSON 응답 (render json:) 사용 - Turbo Stream 아님
- 충돌 해결: 서버 데이터가 더 최신이면 서버 우선, 그렇지 않으면 클라이언트 데이터 적용
- content의 접근 권한 검증: 사용자가 참여 중인 세션의 콘텐츠만 동기화 허용

첨부 이미지

이미지 추가 (Ctrl+V로 붙여넣기 또는 클릭)

JPEG, PNG, GIF, WebP / 최대 10MB

담당자: offline-backend
생성일: 2026년 03월 03일 05:24

활동 로그

  • 팀리드 상태 변경: 리뷰 → 완료

    2026년 03월 03일 05:40:36

  • O
    offline-backend 상태 변경: 할 일 → 리뷰

    2026년 03월 03일 05:30:45

  • O
    offline-backend Sync API 구현 완료 - 테스트 전체 통과

    2026년 03월 03일 05:30:39

    상세 보기

    ## 변경 사항

    ### 1. 라우트 추가 (`config/routes.rb`)
    - `POST /qt/meditations/sync` collection 라우트 추가

    ### 2. Sync 액션 (`app/controllers/qt/meditations_controller.rb`)
    - `sync` 액션 추가 (기존 create/update/organize 미수정)
    - `skip_before_action :verify_authenticity_token, only: :sync` (JSON API용)
    - 접근 권한 검증: `accessible_content_ids` - User의 활성 참여 세션의 qt_content만 허용
    - 충돌 해결: server updated_at > client created_at이면 conflict 반환 (서버 우선)
    - JSON 응답: `{ results: [{ qt_content_id, status: "synced"|"conflict"|"error", ... }] }`

    ### 3. 테스트 (7개 추가)
    - 인증 필수 확인
    - 새 묵상 생성 (synced)
    - 기존 묵상 업데이트 (no conflict → synced)
    - 충돌 처리 (server wins → conflict + server_data)
    - 접근 불가 qt_content → error
    - 빈 배열 → empty results
    - 배치 처리 (2개 동시)

    ### 4. 테스트 결과
    - 525 runs, 1258 assertions, 0 failures, 0 errors (기존 1 error는 ProfilesController Active Storage 관련 기존 이슈)

  • O
    offline-backend 백엔드 Sync API 구현 시작

    2026년 03월 03일 05:26:50