Skip to content

Releases: SoongSilComputingClub/ssccops-web

v0.2.0 — 학술·행사·기획안과 3앱 모노레포

Choose a tag to compare

@swthewhite swthewhite released this 02 Sep 13:43
d4d6e62

두 번째 후속 릴리스입니다. develop에 쌓인 58개 커밋main으로 올렸습니다.

v0.1.x까지 이 저장소는 어드민 앱 하나였습니다. 이번 회차에 모노레포로 전환하고 앱이 셋으로 늘었습니다 — 운영진이 쓰는 어드민, 로그인 없이 열리는 공개 웹사이트, 회원·스터디장이 쓰는 학술 앱입니다.

워크스페이스 무엇 로그인 Worker (prod)
apps/admin 운영관리 어드민 (기존 앱) 전 화면 필수 admin-ssccops
apps/www 공개 웹사이트 — 행사 목록·상세·참가 신청 신청·내 신청만 www-ssccops
apps/lms 학술 — 대시보드·회차·출석·기획안 전 화면 필수 lms-ssccops

앱을 나눈 기준은 로그인입니다. 학술 앱은 전 화면이 로그인 필수라 공개 앱에 얹지 않고 도메인부터 갈랐고, 공개 앱의 본체(행사 목록·상세)는 비로그인 완전 공개입니다.


모노레포 전환

  • pnpm workspace + Turborepo (#138). 어드민 앱 소속 파일 전부를 git mvapps/admin/으로 옮겼습니다 — 히스토리가 보존되고 내용은 한 줄도 바뀌지 않았습니다.
  • packages/form-renderer 추출 (#152). 폼 렌더러를 공유 패키지로 뽑았습니다 — 어드민의 미리보기, 공개 폼, 행사 신청서, 기획안 작성이 같은 렌더러를 씁니다. 두 벌이면 제출자가 본 화면과 검토자가 본 화면이 갈립니다.
  • Worker 이름을 wrangler.jsonc가 아니라 CLI --name으로 (#157). 앱이 늘 때마다 env.dev.name·env.prod.name을 파일마다 박아 넣지 않기 위해서입니다. 이름 규칙은 각 앱 package.json 스크립트 한 줄로 끝납니다.
  • apps/eventsapps/www로 개명 (#160). 행사만 담을 자리가 아니라 공개 웹사이트 전체의 자리가 됐기 때문입니다.

공개 웹사이트 (apps/www)

  • 행사 목록·상세 SSR/OG (#141) — 검색·공유에 걸려야 하는 화면이라 서버 렌더링이고 OG 메타를 냅니다.
  • 로그인 연동과 '내 신청' (#150), 참가 신청 흐름 (#154) — 가입 유도·폼 작성·제출까지. 간편 가입을 신청 흐름 안에 임베드해, 처음 온 사람이 어드민으로 튕겨 나갔다 돌아오지 않아도 됩니다.
  • 셸 정비와 PWA (#167) — 제목·상단 바 재배치.

학술 (apps/lms · apps/admin)

스터디장이 쓰는 화면과 학술국장이 쓰는 화면을 앱으로 갈랐습니다.

스터디장 (apps/lms)

  • 팀원 관리 (#131) · 회차 기록 작성과 출석 체크 (#128) · 출석부 — 회차별 참석 현황 행렬 (#172) · 내 활동 목록·상세 (#188) · 대시보드 (#126)
  • 첫 화면은 신분에 따라 갈립니다 (#228) — 스터디장은 대시보드로, 일반 회원은 기획안 카드 두 장. 스터디장이 아니면 상단 바에 기획안 메뉴 둘만 보입니다 (#224).

학술국장 (apps/admin)

  • 학술관리 도메인 골격 (#122) · 스터디·프로젝트 목록과 활동 상세 (#125) · 회차·출석 승인 (#129) · 회차 이력·상세·출석 통계 (#130) · 대시보드 (#126)
  • 모집 관리 (#127) — 모집 시작과 신청자 선발. 선발은 참가 상태로 그리고 언제든 다시 저장할 수 있습니다 (#209). 신청서 문항 편집으로 바로 연결됩니다 (#193).

기획안

  • 작성·제출과 제출 현황 (#163) · 신규 작성 (#185) · 재제출 (#171 — 수정요청 사유를 먼저 보여주고 이전 답을 프리필합니다)
  • 검토 화면 (#164) — 커리큘럼 표와 승인·수정요청·반려. 검토자는 승인을 누르기 전에 이관 결과를 표로 봅니다.
  • 잠금 사유 노출과 목록의 활동명 표시 (#204)
  • 운영관리에서 회원용 기획안 화면을 걷어냈습니다 (#231) — lms로 옮겨 간 잔재였습니다.

  • 시스템 폼 잠금 표시와 문항 버전 안내 (#132) — 무엇을 고칠 수 있고 무엇이 잠겼는지 화면이 미리 말합니다.
  • 응답 검토 처리 화면 (#133) — 수정요청·반려 사유와 처리 이력.
  • 템플릿 관리 (#134), 다중 응답 설정과 응답 목록 반영 (#135)
  • 계약 문항을 서버 값으로 잠급니다 (#156) — 웹이 문항 구성에서 계약을 역산하던 것을 걷어냈습니다. 역산은 서버가 계약을 바꾸는 순간 조용히 어긋납니다.
  • 학술 모집 폼의 접수 기간은 모집 관리에서만 (#194 · #222) — 폼 편집에서 입력란을 숨기고 접수 시작을 잠급니다. 접수 기간의 주인이 두 곳이면 어느 쪽이 이겼는지 화면으로는 알 수 없습니다.

행사 (운영진)

  • 행사 관리 화면 (#136) — 목록·편집·상태 전이·분류 관리
  • 신청 심사·참가자 관리 (#145)
  • 본문 이미지 첨부 (#148) — R2 presigned 업로드

회원

  • 동아리 가입 시기 입력과 연도 기반 기수 자동 채움 (#214)
  • CSV 이관의 동아리 가입 시기 매핑 전환 (#215) — 안내표와 양식 CSV를 함께 갱신했습니다.
  • 학번 수정과 변경 이력 표시 (#237)

수정

  • 완료된 하위 업무가 '지연'으로 표기되던 문제 (#199)
  • datanull인 200을 오류로 세우지 않습니다 (#197) — 초안이 없는 첫 진입에서 신청이 막히고 있었습니다.
  • 회차 목록 상태 필드명(sttsCd)이 어긋나 활동 상세가 죽던 문제 (#212), 회차 목록 정렬 (#190)
  • 신청서 문항 편집을 같은 탭으로 (#207) — 새 탭이라 뒤로가기가 죽었습니다.
  • 사이드바 '기획안 검토'를 폼에서 학술 묶음으로 (#201), 공개 폼 작성 화면 가로 상한 720px → 860px (#203)
  • 서버 개명 반영 — 커리큘럼 계획일 planYmd (#170), 처리 구분 RSPNS_ 접두사 우회 제거 (#233 · #235), 행사 이미지 publicUrlimageUrl (#218), 업로드가 File.type에 의존하지 않게 (#220)

⚠️ 배포 시 주의

이 저장소의 워크플로는 배포하지 않습니다 — CI만 돕니다. 실제 배포는 Cloudflare Workers Builds가 GitHub를 직접 보고 수행하므로, 아래는 전부 Cloudflare 대시보드에서 사람이 해야 하는 일입니다.

1. 손댈 곳은 어드민 하나입니다 — 나머지 넷은 이미 붙어 있습니다

이 PR의 커밋에 붙은 Workers Builds 체크로 확인한 현재 연결 상태입니다.

Workers Builds 프로젝트 상태
dev-admin-ssccops · dev-www-ssccops · dev-lms-ssccops ✅ 연결됨 · 통과
www-ssccops · lms-ssccops (prod) ✅ 연결됨 · 통과
admin-ssccops (prod) 없습니다
ssccops (prod 어드민, 옛 이름) ⚠️ main에 묶여 있고, 저장소 루트를 빌드 루트로 봅니다

어드민 앱은 저장소 루트에서 apps/admin/으로 내려갔습니다. 그런데 prod 어드민을 짓는 ssccops 프로젝트는 아직 루트를 보고 있고, 그 자리에는 이제 Next 앱이 없습니다 — 이 릴리스가 main에 들어가는 순간 그 빌드는 실패합니다.

다행히 실패하는 쪽이 안전한 실패입니다. 빌드가 깨지면 배포도 없으므로, 고칠 때까지 prod 어드민은 계속 v0.1.1을 서빙합니다. 서비스가 끊기는 것이 아니라 새 버전이 안 올라갈 뿐입니다.

고치는 순서:

  1. ssccops 프로젝트의 빌드 루트 디렉터리를 apps/admin으로 바꿉니다.
  2. 스크립트 이름 충돌을 확인합니다. apps/admindeploy:prod는 이제 --name admin-ssccops를 넘깁니다 — 빌드 프로젝트가 짓는 스크립트(ssccops)와 이름이 다릅니다. 둘 중 하나로 맞춰야 합니다:
    • 프로젝트를 admin-ssccops로 새로 만들고 라우트·커스텀 도메인을 옮긴 뒤 옛 ssccops Worker를 정리하거나(다른 두 앱과 이름 규칙이 같아집니다), 또는
    • apps/admin/package.json--namessccops로 되돌립니다(도메인 바인딩을 건드리지 않아도 됩니다).
  3. dev 어드민(dev-admin-ssccops)은 이미 새 구조로 통과하고 있으므로 루트 디렉터리 설정이 맞는 예시로 참고하면 됩니다.

2. prod 프로젝트가 어느 브랜치를 보는지 확인하세요

www-ssccops·lms-ssccops(prod)의 빌드가 develop 커밋에서 돌고 있습니다. 프로덕션 브랜치 설정인지 프리뷰 빌드인지는 저장소 쪽에서 구별되지 않으므로, 두 프로젝트의 Production branch가 main인지 대시보드에서 확인하세요. develop으로 돼 있으면 이 릴리스와 무관하게 prod가 이미 develop을 따라가고 있다는 뜻입니다.

wrangler.jsonc·open-next.config.ts는 세 앱 모두 준비돼 있고, 도메인 계획은 apps/www가 루트 도메인(sscc.club), apps/lmslms.sscc.co.kr입니다.

3. 환경변수 — 앱마다 따로입니다

세 앱이 각각 .env.example을 갖습니다. 공통은 둘입니다.

NEXT_PUBLIC_API_BASE_URL        # 셋 다 같은 ssccops-server
NEXT_PUBLIC_SUPABASE_URL        # 셋 다 같은 Supabase 프로젝트
NEXT_PUBLIC_SUPABASE_ANON_KEY   #   ↳ 같은 계정이 같은 회원으로 식별돼야 한다

앱별로 더 필요한 것:

  • apps/adminNEXT_PUBLIC_PUBLIC_FORM_ORIGIN(공개 폼 링크의 오리진, 비우면 현재 오리진)
  • apps/www · apps/lmsNEXT_PUBLIC_ADMIN_ORIGIN(가입·계정 연결 화면이 있는 곳). 비워 두면 링크 없이 안내 문구만 나옵니다 — 없는 화면으로 보내지 않기 위한 기본값입니다.

NEXT_PUBLIC_*빌드 타임에 인라인됩니다. 값을 바꾸면 재배포해야 반영됩니다.

4. Supabase Redirect URLs에 새 오리진 둘을 등록해야 합니다

https://<www 도메인>/auth/callback
https://<lms 도메인>/auth/callback

빠뜨리면 오류가 나지 않고 조용히 어긋납니다. Supabase(GoTrue)는 등록되지 않은 redirect_to를 거부하는 대신 Site URL로 폴백하므로, 새 앱에서 시작한 로그인이 어드민 도메인에서 끝나고 그쪽에는 PKCE code_verifier 쿠키가 없어 exchange_failed로 죽습니다. 어드민이 예전에 실제로 밟은 함정이고(ssccops#84), 오리진이 셋으로 늘어 다시 밟기 쉽습니다.

등록이 맞는지는 밖에서 확인할 수 있습니다 — Location이 보낸 값 그대로면 통과, Site URL이면 폴백된 것입니다:

curl -sD - "$SUPABASE_URL/auth/v1/verify?type=magiclink&token=bogus&redirect_to=<URL인코딩>" | grep -i '^location:'

5. 서버의 CORS 허용 오리진에도 새 오리진 둘을 더해야 합니다

ssccops-serverPROD_FRONTEND_URL쉼표로 여러 개를 받습니다. 세 앱의 오리진을 모두 넣으세요.

빠뜨리면 증상이 '서버가 꺼진 것'과 똑같이 보입니다 — 조회는 대부분 SSR이라 그대로 열리는데, 브라우저에서 직접 서버를 부르는 화면(기획안 자동 저장·제출, 행사 신청서 초안 저장·제출)만 CLIENT_NETWORK_ERROR로 떨어집니다.

6. 함께 배포

ssccops-server v0.2.0과 함께 배포해야 합니다. 학술·행사·기획안 화면은 이 릴리스의 API가 있어야 동작하고, 회원 응답의 systemJoinDate·검토 이력의 rvwPrcsSeCd·행사 이미지의 imageUrl은 옛 서버와 조합하면 빈칸이 됩니다.

Full Changelog: v0.1.1...v0.2.0

v0.1.1 — 모바일 대응과 권한 기반 승인 표기

Choose a tag to compare

@swthewhite swthewhite released this 21 Aug 15:43
8d1d58a

첫 프로덕션 릴리스(v0.1.0) 이후의 첫 후속 릴리스입니다. develop에 쌓인 21개 커밋main으로 올렸습니다.

이번 회차의 큰 줄기는 모바일 대응입니다. 예전에는 body { min-width: 1024px }가 데스크톱 전용을 강제하고 있어, 휴대폰으로 열면 화면이 가로로 밀린 채 조작해야 했습니다.


모바일 반응형

경계는 lg(1024px) 하나뿐입니다. 예전의 min-width 값을 그대로 브레이크포인트로 삼았으므로 lg 이상은 정의상 예전과 같은 화면입니다 — 데스크톱 사용자에게는 바뀐 것이 없습니다.

  • 기반 구축 (#85) — 전역 뷰포트, 관리자 셸(좁은 화면에서는 사이드바 대신 드로어), 공용 컴포넌트, 대시보드. 메뉴 목차와 권한 판정은 한 벌만 두어 사이드바와 드로어가 갈리지 않게 했습니다.
  • 화면별 대응 — 승인함 (#86) · 폼 응답 (#87) · 가입 흐름 (#91) · 운영 조회 (#92) · 회원 조회 (#93) · 폼·응답 조회 (#94) · 등록·수정 (#95) · 역할·권한·CSV 이관 위저드 (#96)
  • 입력란 글자를 좁은 화면에서 16px로 (#105). iOS Safari는 16px 미만인 입력란에 포커스하면 화면을 자동 확대하고 그 확대가 스스로 돌아오지 않습니다 — 첫 칸에 입력하는 순간 폼 전체가 커진 채로 남았습니다. 미관이 아니라 동작 문제라 공용 입력 컴포넌트에 규칙을 걸고 AGENTS.md에 남겼습니다.
  • 대시보드 '내 업무 목록' 머리줄이 좁은 화면에서 글자 단위로 깨지던 것 (#103)

열 수가 많은 표(GridTable)는 lg 미만에서 카드로 바뀝니다. 아직 전 화면이 대응된 것은 아닙니다min-width 제거로 드러난 나머지 화면은 후속 이슈로 남아 있습니다.

PWA

  • 매니페스트와 iOS 설치 메타 (#108) — 홈 화면에 앱으로 설치할 수 있습니다
  • 매니페스트를 인증 가드에서 제외 (#110). 가드에 걸려 로그인으로 리다이렉트되는 바람에 설치 자체가 되지 않았습니다
  • 앱 아이콘 (#106) → 동아리 공식 로고로 교체 (#113)

서비스 워커·오프라인·푸시는 이번 범위 밖입니다.

권한 · 문구

  • 승인자 표기를 서버 결재 권한 기반으로 전환하고 투표 버튼을 권한으로 잠금 (#115). 서버가 승인·투표 자격을 권한 시스템으로 통합(ssccops-server#123)하면서, 웹이 들고 있던 승인자 어휘 하드코딩(AUTZR_ROLE_NM 4종)을 걷어냈습니다 — 권한 이름은 화면에서 바뀌는 운영 데이터라 사전을 웹에 두면 개명 즉시 어긋납니다. 투표 버튼은 이제 APPROVAL_VOTE 권한으로 미리 잠깁니다(예전에는 눌러 403을 보고서야 자격이 없다는 것을 알았습니다).
  • 화면 문구 윤문 (#117). AI 번역투를 걷어내려 훑었는데 실제로 걸린 것은 잘못된 안내였습니다 — 시스템에 없는 역할명("최고운영자")을 안내하던 6곳, 시스템에 없는 판정 기준("국장 이상")을 설명하던 2곳, 요구 권한을 "운영진 권한"으로 뭉뚱그려 무엇을 받아야 하는지 알 수 없던 17곳. 회원 도메인이 쓰던 형식(업무 관리(WORK_MANAGE) 권한이 필요합니다)으로 통일하고, 화면에 노출된 개발 용어(@RequireAuthority·PK·"회원 도메인")를 사용자의 말로 옮겼습니다. 톤·용어 기준은 AGENTS.md에 남겼습니다.
  • 권한 분리를 화면에 반영하고 팀 공유용 AGENTS.md 작성 (#82)

운영

  • 업무·하위 업무·회의 소프트 삭제 연동 (#119)

⚠️ 배포 시 주의

ssccops-server v0.1.1과 함께 배포해야 합니다. #115가 서버의 승인자 응답 계약(직위 코드 → 결재 권한)에 맞춰져 있어, 옛 서버와 조합하면 하위 업무 유형 화면의 승인자 표시가 빈칸이 됩니다.

Full Changelog: v0.1.0...v0.1.1

v0.1.0 — 첫 프로덕션 릴리스

Choose a tag to compare

@swthewhite swthewhite released this 16 Aug 16:23
53f8110

SSCC 지원서·운영 관리 프론트엔드의 첫 프로덕션 릴리스입니다. develop 에 쌓인 45개 커밋main 으로 올렸습니다.

Next.js 16 / React 19 / Tailwind CSS 4 / Zustand / Supabase Auth, Cloudflare Workers 에 OpenNext 로 배포합니다.


인증 · 권한

  • Supabase Google OAuth 로그인 (#1) 과 라우트 가드
  • 회원가입 화면 — 폼 검증과 오류 처리 (#3), 로그인 세션 서버 연동 (#4)
  • 역할 기반 화면 권한 (#29) — 서버가 내려주는 capabilities 로 기능 노출을 제어합니다. 화면이 역할을 보고 스스로 판정하지 않으므로 버튼과 실제 인가가 갈리지 않습니다.
  • 역할별 권한 관리 화면 (#32) — 권한 트리 부여·회수. 저장 전 미리 보기를 위해 서버와 같은 펼침 규칙을 트리 간선으로 재현합니다.
  • 회원·CSV 이관 메뉴에 MEMBER_MANAGE 권한 게이트 (#52)
  • 운영관리 접근 권한 수정 (#71)
  • OAuth 복귀 목적지를 쿠키로 전달 (#77) — redirectTo 에서 ?next= 쿼리를 제거했습니다. 쿼리를 붙이면 Supabase 에 등록한 Redirect URL 과 일치하지 않아 Site URL(보통 localhost)로 조용히 폴백하는 결함이 있었습니다.

  • 폼 목록·상세 (#7) — API 계층 분리와 필터의 URL 반영
  • 폼 편집기와 자동 저장 (#8)
  • 라벨 관리·지정 (#10), 접수 상태 전환·복제 (#9)
  • 공개 폼 — 인증 게이트 (#11, 가입 후 원래 폼으로 복귀), 응답 제출·자동 저장 (#12)
  • 응답 목록·상세·상태 변경 (#13) — 상세의 이전/다음 이동은 서버가 내려주는 이웃 식별자를 씁니다(URL 로 바로 열어도 이동이 죽지 않도록)
  • 폼 도메인 서버 계약 감사와 잔재 정리 (#14)

회원

  • 회원 목록·상세 (#46), 정보 수정·내 계정 프로필 수정 (#47)
  • 등급·상태 변경 (#48), 역할 부여·종료 (#50), 변경 이력 화면 (#51)
  • 역할 목록·수정·역할 분류 관리 (#49)
  • CSV 회원 이관 위저드 (#57) — 클라이언트 시뮬레이션을 걷어내고 서버 검증에 연동했습니다. CSV 파싱은 서버 한 곳에만 두어, 따옴표로 감싼 헤더에서 해석이 갈리는 문제를 없앴습니다.
  • 이관 회원 계정 연결 화면 (#58) — 가입 화면에서 기존 회원으로 연결하는 경로
  • 회원·역할 목 스토어 제거 (#54)

운영

  • 업무 목록·상세·등록 (#30), 하위 업무 등록 (#36)·상세·전이·체크리스트 (#39)·전체 조회 (#41)
  • 하위 업무 유형 관리 (#34)
  • 승인함 조회·투표·승인·반려 (#45)
  • 회의 등록·조회 (#56)
  • 운영 대시보드 (#60), 운영 통합 페이지 (#63)
  • 운영 등록의 담당자를 서버 후보 목록에서 선택 (#53)

⚠️ 배포 시 주의

dev/prod 배포 환경이 분리됐습니다

이 릴리스에 Cloudflare Workers 의 dev/prod 분리가 포함됩니다. wrangler.jsonc 에 이름을 하드코딩하던 것을 Wrangler named environments 로 바꿨습니다.

"env": {
  "dev":  { "name": "dev-ssccops" },   // 기존 배포 유지
  "prod": { "name": "ssccops" }
}

환경 블록에서 name 을 명시하면 Wrangler 가 -<env> 접미사를 붙이지 않고 그대로 씁니다. main·assets·compatibility_* 는 상속되고, vars 와 바인딩류는 상속되지 않으므로 추가할 때는 각 env 블록에 따로 써야 합니다.

Workers Builds 의 Production/Preview 로는 이걸 대신할 수 없습니다 — Build variables 가 Worker 당 한 세트뿐이라 production/preview 로 나뉘지 않는데, NEXT_PUBLIC_* 값은 Next 가 빌드 타임에 인라인해 버리므로 dev/prod 가 서로 다른 백엔드를 가리킬 수 없습니다. 그래서 Worker 를 둘로 나눴습니다.

대시보드 설정이 필요합니다

dev-ssccops (기존, 설정 수정) ssccops (신규 생성)
Production branch develop main
Build command pnpm exec opennextjs-cloudflare build -e dev pnpm exec opennextjs-cloudflare build -e prod
Deploy command pnpm exec opennextjs-cloudflare deploy -e dev pnpm exec opennextjs-cloudflare deploy -e prod
Non-production branch builds 켤 경우 ... upload -e dev 끄기
Build variables dev 용 NEXT_PUBLIC_* prod 용 NEXT_PUBLIC_*

기존 dev-ssccops-e dev 를 넣지 않으면 다음 빌드가 ssccops-web 이라는 의도치 않은 세 번째 Worker 로 나갑니다.

Supabase · CORS

Google 프로바이더의 Redirect URL 은 경로까지 포함해서 등록해야 합니다. 오리진만 등록하면 Supabase 가 Site URL(보통 localhost)로 조용히 폴백해, 배포 환경에서 로그인이 로컬로 튕깁니다.

https://dev-ssccops.sscc-webservice.workers.dev/auth/callback
https://ssccops.sscc-webservice.workers.dev/auth/callback

서버의 frontend.url(CORS 허용 오리진)에도 prod 오리진 추가가 필요합니다 — 쉼표로 여러 개 넣을 수 있습니다.

필요한 빌드 변수

NEXT_PUBLIC_SUPABASE_URL · NEXT_PUBLIC_SUPABASE_ANON_KEY · NEXT_PUBLIC_API_BASE_URL · NEXT_PUBLIC_PUBLIC_FORM_ORIGIN(선택)

NEXT_PUBLIC_API_BASE_URL 이 비면 인증부터 폼 도메인 전 화면까지 목 데이터 폴백 없이 빈 화면이 됩니다.

스크립트 변경

pnpm run deploydeploy:dev / deploy:prod 로 나뉘었습니다. 겸사겸사 pnpm 내장 명령과 충돌하던 pnpm deploy 이름도 피했습니다.

pnpm run preview       # build -e dev && preview -e dev
pnpm run deploy:dev
pnpm run deploy:prod

알려진 제약

  • prod Worker(ssccops)는 아직 생성돼 있지 않습니다 — 위 대시보드 설정이 선행돼야 프로덕션 배포가 시작됩니다.
  • 폼 응답 목록에 페이징이 없습니다(서버 결정에 맞춤).