個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗스마트캠퍼스 과제, 직접 적은 과제, 시험 일정을 마감 순으로 모아 보는 학기 플래너입니다. 단독 웹으로도, 노션 페이지에 임베드해서도 씁니다. 같은 보드 코드를 입력한 기기끼리 과제가 동기화됩니다.
main push 시 자동 배포)보기
과제
시험
스마트캠퍼스 강의 영상 (할 일)
기타 일정 (할 일 · 휴강 등)
T는 기타 · 할 일로 열림)과목 줄임말 · 짧은 이름
공부 중 알림 (폰)
새 과제 알림
스마트캠퍼스 공지
미제출
캘린더로 보내기
calendar-feed Edge Function, 읽기 전용 열쇠, 새로 만들면 이전 주소는 막힘).반복
노션 간단 모드
?compact를 붙이면 다가오는 일정 6개만 크게 보여요. 노션 임베드용.마감 알림
공부 모드 (왼쪽 위 일정 공부 글자, 단축키 S)
오늘 · 내일 · 이번 주로 내일이나 이번 주(모레~7일 뒤) 중 고른 날에 넣어 두면 그날 오늘 공부에 나와요. 지난 7일 안에 넣고 못 한 계획은 오늘 공부 아래 "지난 계획"으로 흐리게 나오고 ← 버튼으로 오늘로 옮겨요친구 버튼(공부 중인 친구 수, 받은 신청이 있으면 +N). 닉네임을 정하면 친구 코드(XXXX-XXXX)가 생기고, 친구 코드로 신청 → 상대가 수락하면 서로 친구가 돼요 (서로 신청하면 바로). 오늘 공부 시간과 지금 공부 중인지만 보이고 과목 · 계획은 안 보여요. 친구를 끊으면 양쪽에서 함께 지워져요. 코드를 새로 만들면 이전 코드로는 신청할 수 없어요떠 있는 타이머
친구 랭킹
폰 홈 화면 위젯
upcoming 60), 안드로이드는 위젯 앱에서 위젯마다 따로widgets/ios/S-DAY.js). 홈 화면 · 잠금 화면 위젯. 위젯 편집의 Parameter에 study · timer · upcoming을 적으면 한 스크립트로 다른 종류도 돼요widgets/android의 위젯 앱 (GitHub Actions가 빌드해서 Releases의 android-widget에 S-DAY-widget.apk로 올림). 앱에 위젯 주소를 붙여 넣고 홈 화면에서 S-DAY 위젯 세 가지 중 골라 추가분류 (학기 · 기간 · 넓은 의미의 과목)
지금(오늘이 기간 안에 드는 분류 + 기간 없는 분류를 모두 합쳐서) · 분류 하나 · 미분류(어느 분류에도 없는 것) · 전체, 맨 아래 분류 관리. 폰에서는 필터 안의 칩+ 과목 추가로 새 과목찾기
/, Esc로 닫기), 과목 필터, 완료 숨기기, 중요한 과제만 보기그 밖에
S 일정 ↔ 공부, 1 2 탭(목록 · 캘린더 / 오늘 · 기록), N 새 과제, E 새 시험, T 새 할 일, / 검색, 공부 · 오늘에서 Space 타이머 시작 · 끝내기, Esc 닫기index.html 화면 뼈대 (연결 화면, 앱, 상세 패널, 설정)
assets/style.css 디자인 토큰과 스타일 (흰 바탕 + 형광펜 콘셉트)
assets/js/app.js 시작, 화면 그리기, 이벤트, 단축키
assets/js/views.js 목록 / 캘린더 보기
assets/js/store.js 상태, 낙관적 업데이트, 동기화, 되돌리기 삭제
assets/js/api.js Supabase RPC와 Edge Function 호출, 오류 문구
assets/js/util.js 날짜·학기 계산, 과목 색, DOM 헬퍼
assets/js/canvas.js 스마트캠퍼스 데이터 변환, 북마클릿
assets/js/picker.js 달력, 시계 다이얼
assets/js/push.js 마감 알림 켜기 / 끄기
assets/js/subject-picker.js 과목 고르기 (색 점 목록, 찾기, 새 과목 추가)
assets/js/study.js 공부 모드 (오늘: 타이머 · 계획 · 타임라인, 기록: 주간 · 월간, 친구)
sw.js, manifest.webmanifest PWA (캐시, 알림 표시, 바로가기)
supabase/migrations/ DB 스키마와 함수
supabase/functions/canvas-sync/ 스마트캠퍼스 피드 동기화
supabase/functions/push-notify/ 마감 알림 · 한 번짜리 알림 발송 (10분마다)
supabase/functions/calendar-feed/ 캘린더 구독용 .ics
브라우저 (노션 임베드 / 모바일 브라우저)
│ POST /rest/v1/rpc/dday_* (publishable key + 보드 코드)
│ POST /functions/v1/canvas-sync (보드 코드)
▼
Supabase
├─ public.dday_* 함수 ← 외부에서 호출 가능한 입구
├─ Edge Function canvas-sync ← Canvas 피드를 읽어 병합
├─ pg_cron ← 하루 3번 canvas-sync 실행
└─ app 스키마 (boards, tasks) ← API 비노출, 직접 접근 차단
보드마다 두 가지 중 하나로 자동 연동합니다. 둘 다 canvas-sync Edge Function이 하루 3번(07:03, 13:03, 21:03 KST), 그리고 상단 새로고침 버튼을 누르면 바로 실행합니다 (다른 기기 변경 사항도 함께 불러옴).
스마트캠퍼스 계정 → 설정 → 승인된 통합 → + 새 액세스 토큰에서 만든 토큰을 설정에 붙여 넣으면, 서버가 Canvas REST API로 직접 읽습니다.
GET /api/v1/courses?enrollment_state=active&enrollment_type=student → 과목별 GET /api/v1/courses/:id/assignments?include[]=submissiontoken_invalid로 기록하고, 화면 위쪽 안내와 함께 알림을 켠 기기로 한 번 알려 줍니다.https://호스트만, IP · localhost 안 됨)를 바꾸면 그 주소로 동기화합니다 (boards.canvas_api_origin, 기본 https://canvas.ssu.ac.kr). 토큰도 그 학교 것이어야 하고, 강의 영상은 learningx를 쓰는 학교만 가져옵니다.sessionless_launch를 열어 learningx가 주는 임시 쿠키를 받고 GET /learningx/api/v1/courses/:id/modules?include_detail=true에서 영상 항목(attendance_item)의 기한과 시청 완료 여부를 읽습니다. 임시 쿠키는 실행 중 메모리에만 두고 저장하지 않습니다. 진도를 세지 않는 영상(omit_progress)은 봤는지 알 수 없어서 넣지 않고, 새로 넣는 건 아직 안 봤고 기한이 남은 것만입니다.스마트캠퍼스 캘린더 화면의 "캘린더 피드" 주소(…/feeds/calendars/user_….ics)를 등록하면 피드를 읽어 과제를 넣습니다. 토큰을 쓰지 않을 때의 대안입니다.
UID:event-assignment-*인 항목만 과제로 봅니다.SUMMARY의 과제명 [과목명 (학수번호)]를 과제명과 과목으로 나눕니다.*_custom 플래그로 기억해 이후 동기화가 덮어쓰지 않습니다.토큰을 쓰지 않을 때 피드의 30일 제한과 제출 정보 부족을 보완하려고, 설정에 "D-day로 가져오기" 북마클릿을 둡니다. 토큰을 연결하면 이 항목은 숨깁니다.
/api/v1/courses와 과목별 /api/v1/courses/:id/assignments?include[]=submission을 모읍니다. 토큰이 필요 없습니다.postMessage로 데이터를 넘기고, 앱에서 미리 보기 후 dday_canvas_import로 병합합니다./api/v1/planner/items)를 브라우저에서 열어 저장한 파일을 올려도 됩니다. 이 경우 파일 이름 없이 제출 여부까지만 가져옵니다.피드 주소는 로그인 없이 일정을 볼 수 있는 개인 주소라서, DB의 잠긴 스키마에만 저장하고 API로 다시 읽어 갈 수 없게 했습니다. 예약 실행 인증값은 Supabase Vault에 둡니다.
처음에는 노션 HTML 블록에 파일을 직접 넣었지만, 블록이 샌드박스 iframe으로 렌더링되면서 두 가지가 막혔습니다.
<form> 제출이 동작하지 않음 → 폼 태그 대신 버튼 클릭 이벤트로 처리fetch가 차단됨 → 위젯을 Vercel에 올리고 노션에서는 URL 임베드로 표시로그인 없이 쓰기 위해 보드 코드가 곧 열쇠인 구조입니다.
app 스키마에 두고, anon/authenticated 역할의 접근 권한을 모두 회수했습니다. RLS도 켜 두고 정책은 두지 않았습니다.SECURITY DEFINER 함수 5개만 실행할 수 있고, 각 함수는 보드 코드를 검증한 뒤 해당 보드의 데이터만 다룹니다.Supabase 보안 어드바이저는 "로그인 없이 실행 가능한 SECURITY DEFINER 함수" 경고를 띄우는데, 위 구조상 의도된 것입니다.
모든 함수는 POST {SUPABASE_URL}/rest/v1/rpc/{함수명}으로 호출하고, 헤더에 apikey를 넣습니다.
| 함수 | 인자 | 반환 | 설명 |
|---|---|---|---|
dday_create_board | 없음 | text | 새 보드를 만들고 코드 반환 |
dday_list | p_code | jsonb 배열 | 보드의 과제 목록 (마감 순) |
dday_upsert | p_code, p_task | jsonb | 과제 추가 또는 수정 |
dday_delete | p_code, p_id | 없음 | 과제 삭제 (스마트캠퍼스 과제는 숨김) |
dday_import | p_code, p_tasks | int | 여러 과제 일괄 추가 (이미 있는 id는 건너뜀) |
dday_set_canvas_feed | p_code, p_url | jsonb | 피드 주소 등록, 빈 값이면 해제 |
dday_set_canvas_token | p_code, p_token | jsonb | 액세스 토큰 등록 (Vault에 저장), 빈 값이면 해제 |
dday_canvas_status | p_code | jsonb | 연동 여부(feed, token, tokenHint), 마지막 동기화 시각, 오류 |
dday_push_subscribe | p_code, p_sub, p_leads | jsonb | 이 기기 알림 켜기 또는 알림 시점 변경 (p_sub은 PushSubscription.toJSON(), p_leads는 분 단위) |
dday_push_unsubscribe | p_code, p_endpoint | 없음 | 이 기기 알림 끄기 |
dday_push_status | p_code, p_endpoint | jsonb | 이 기기 알림 상태와 알림 시점 |
dday_study | p_code, p_from | jsonb | 공부 기록(p_from 이후, 최대 40일), 날짜별 합계(400일), 최근 계획, 하루 목표, 프로필 |
dday_svc_* 함수는 Edge Function(service_role) 전용이라 외부에서 호출할 수 없습니다.
dday_upsert의 memo, starred는 보내지 않으면 기존 값을 유지합니다.
Edge Function POST {SUPABASE_URL}/functions/v1/canvas-sync에 { "code": "<보드 코드>" }를 보내면 그 보드를 바로 동기화하고 { added, updated, total }을 돌려줍니다.
과제 형식:
{ "id": "lzx1a2b3", "subject": "데이터베이스", "title": "ER 다이어그램", "due": "2026-10-20T14:59:00.000Z", "done": false,
"memo": "", "starred": false, "source": "manual", "link": null, "doneAt": null, "createdAt": "…" }
Edge Function push-notify에 { "code", "endpoint" }를 보내면 그 기기로 테스트 알림을 보냅니다. 예약 실행(10분마다)은 x-cron-secret 헤더로 인증하고, 알림 시점이 지난 과제마다 가장 가까운 시점 하나만 보낸 뒤 기록합니다. VAPID 개인 키는 Vault(dday_vapid_private)에만 있고, 공개 키는 assets/js/push.js와 함수에 들어 있습니다.
오류는 invalid_code, too_many_tasks(보드당 500개 제한), invalid_import, invalid_feed_url, no_feed, feed_* 메시지로 돌아옵니다.
supabase/migrations/의 SQL을 순서대로 실행합니다. canvas_cron 마이그레이션의 함수 URL은 자기 프로젝트 ref로 바꿉니다.supabase/functions/canvas-sync와 push-notify를 Edge Function으로 배포합니다. 알림을 쓰려면 VAPID 키를 만들어(npx web-push generate-vapid-keys) 개인 키는 Vault에 dday_vapid_private로 넣고, 공개 키는 assets/js/push.js와 함수의 VAPID_PUBLIC에 넣습니다. 인증은 함수 안에서 보드 코드와 Vault 값으로 하므로 JWT 검증은 끕니다.assets/js/api.js 상단의 PROJECT(Supabase 프로젝트 주소)와 API_KEY(publishable key)를 자기 프로젝트 값으로 바꿉니다./embed로 그 주소를 넣습니다.빌드 과정은 없지만 ES 모듈을 쓰기 때문에 로컬에서는 python3 -m http.server처럼 간단한 서버로 열어야 합니다.
생각 중인 것:
boards.owner_id를 이용한 로그인 계정 연결dday_subject_colors / dday_set_subject_color | p_code, p_subject, p_color | jsonb | 바꾼 과목 색 목록 · 색 바꾸기(#RRGGBB, null이면 기본 색) |
dday_subject_prefs / dday_set_subject_order | p_code, p_order | jsonb | 과목 색 · 순서 · 직접 추가한 과목 함께 읽기 · 순서 저장(과목 이름 배열, 최대 200개) |
dday_set_custom_subjects | p_code, p_list | jsonb | 직접 추가한 과목 목록 저장 (최대 100개, 모든 기기에서 같게) |
dday_set_groups | p_code, p_data | jsonb | 분류 목록(groups), 과목 소속(members, "*"은 모든 분류), 쓰지 않는 과목(inactive), 삭제한 자동 학기(removed) 저장 |
dday_rename_subject | p_code, p_from, p_to | jsonb | 과목 이름 바꾸기 (과제 · 공부 기록 · 계획 · 색 · 순서 · 분류 소속까지, 스마트캠퍼스 원래 이름은 subject_aliases로 새 이름에 연결) |
dday_widget_key / dday_widget_status | p_code | text / boolean | 위젯 열쇠 새로 만들기(이전 열쇠는 막힘) · 있는지 |
dday_timer / dday_timer_set | p_code, p_state | jsonb | 기기 사이 타이머 상태 읽기 · 저장 (updatedAt이 더 새로울 때만 덮어씀) |
dday_study_range | p_code, p_from, p_to | jsonb 배열 | 기간 안의 공부 기록 (최대 45일, 기록 탭에서 예전 주 · 달) |
dday_study_add / dday_study_remove | p_code, 기록 / p_id | jsonb / 없음 | 공부 기록 저장(같은 id면 덮어씀) · 지우기(p_removed=false면 되돌리기) |
dday_study_plan / dday_study_plan_remove | p_code, 계획 / p_id | jsonb / 없음 | 공부 계획 저장 · 지우기 |
dday_study_goal | p_code, p_minutes | int | 하루 목표(분) |
dday_study_live | p_code, p_on | 없음 | 친구에게 "공부 중" 표시 (켜 두면 15분마다 연장, 끊기면 저절로 꺼짐) |
dday_study_profile / _profile_hide / _new_code | p_code, p_nickname | jsonb | 닉네임 정하기(친구 코드 생성) · 공개 그만하기 · 코드 새로 만들기 |
dday_friends / dday_friend_add / dday_friend_remove | p_code, p_friend_code | jsonb 배열 | 친구 목록(수락된 친구만: 닉네임, 오늘 · 이번 주 · 이번 달 초, 공부 중 시작 시각) · 신청(상대가 먼저 신청했으면 바로 친구) · 끊기 · 거절 · 신청 취소(양쪽 모두 지움) |
dday_friend_accept / dday_friend_requests | p_code, p_friend_code / p_code | jsonb | 받은 신청 수락 · 받은 신청과 보낸 신청 목록 |
dday_set_canvas_origin | p_code, p_origin | jsonb | 학교 LMS 주소 바꾸기 (빈 값이면 숭실대) |