코드로그 운영을 중단하였다.
이유는 aws 라이트세일 무료플랜이 만료되어 가는 것도 있고 aws저용량 버전에서
사용량에 따른 과금 플랜으로 업그레이드하려고 했으나 해킹이나 트래픽이 문제가 생겨
과다청구되거나 문제가 발생한다는것을 알게 되었다 물론 안 일어날수도 있지만 노파심에
위험 부담을 줄이고 새로운 방식을 채택하고자 하였다.
하지만 새로운 방식으로 배포를 하려고 찾아보니 프로젝트의 구성을 바꿔야 했고 이를
유지하기 위해서는 다른 클라우드형 서비스를 찾아야 했는데 가입이 안되는 문제나 인수되어
이전된경우 등 많은 문제가 발생 이에 서비스를 중단하고 내용을 기록하여 깃 레포에 작성하고
마무리 하는것으로 결정했다.
경험으로 구성한 프로젝트이기 때문에 다른 부분은 아쉽지만 앞으로 개발에 방향성을
잘 찾은거 같아 만족한다.
다만 스프링을 기반으로한 프로젝트는 데이터베이스가 별도로 필요한 서비스들이 대부분인데
많은 클라우드 서비스를 무료로 이용하기에는 부적합 하여 가장 많이 개발에 사용한 방식인
next.js나 자바스크립트 클라우드 플레어를 기반으로 한 프로젝트 위주로 개발을 이어나갈
예정이다 운영이나 관리면에서 수월하고 비용부담도 없기 때문에 이 형태로 계속할듯 하다.
링크는: https://ppnation.heohyuk.workers.dev/
npm outdated로 확인된 것이 전부 semver 범위 내 패치라 npm update 한 번으로 끝냈다.
astro 7.2.9 → 7.2.10wrangler 4.127.0 → 4.127.1@astrojs/cloudflare 14.2.5 → 14.2.6@astrojs/react 6.0.4 → 6.0.5package.json의 ^ 범위는 그대로고 package-lock.json만 바뀐다.
npm run build 통과 확인 후 커밋.
원래 자료실(/resources)은 D1 테이블 resources + R2 버킷(파일 저장) 구조였다.
자료 하나 추가하려면 DB에 INSERT하거나 별도 관리 UI가 필요했고, R2도 계정에서
활성화해야 해서 아직 503만 뱉는 상태였다.
퍼블릭 GitHub 레포 하나를 자료 저장소로 쓰고, 사이트는 요청 시점에 그 레포의
resources.json(매니페스트)을 읽어 목록을 렌더링한다. 자료 추가 = 레포에 커밋.
resources.json){
"items": [
{
"title": "1강 - 오리엔테이션",
"description": "강의 소개와 환경 세팅",
"category": "강의노트",
"file": "files/lecture-01.pdf"
},
{
"title": "MDN JavaScript 가이드",
"category": "참고자료",
"url": "https://developer.mozilla.org/..."
}
]
}
file = 레포 기준 상대경로(파일 자료), url = 외부 링크. 둘 중 하나 필수.category 생략 시 기타.src/lib/resources.ts)요청 시점에 raw.githubusercontent.com/<repo>/main/resources.json을 fetch해서
정규화한다. 핵심은:
..)·절대경로·비 http(s) 링크 필터링file → 다운로드 링크로 변환 (뒤 6번에서 프록시 경유로 바뀜)페이지(src/pages/resources/index.astro)는 prerender = false(SSR)로 두고
응답에 cache-control: public, max-age=300, stale-while-revalidate=600을 붙였다.
레포에 커밋하면 재배포 없이 GitHub CDN 캐시(~5분) + 우리 응답 캐시(5분)
안에 사이트에 반영된다. 실제로는 커밋 후 2분 내에 떴다.
lib/db.ts의 listResources에 @deprecated만 달고, R2용 /api/files/[...key].ts도
되돌릴 때를 위해 코드만 남겼다(미사용).
npm run deploy가 code 10000(인증 실패)로 죽었다. wrangler whoami를 찍어보니
다른 프로젝트(budge) 계정으로 로그인돼 있었다.
~/.bashrc에 이렇게 전역 export가 박혀 있었다.
export CLOUDFLARE_API_TOKEN=<budge 계정 토큰>
budge 작업할 때 넣고 안 뺀 것. 이게 모든 WSL 셸에 상속되고,
wrangler 인증 우선순위가 CLOUDFLARE_API_TOKEN 환경변수 > OAuth 로그인이라,
wrangler login을 새로 해도 이 변수가 안 빠지면 계속 그 토큰을 썼다.
~/.bashrc에서 그 줄 삭제 (백업 남김)budge/.envrc(gitignore)로 옮김 → 필요할 때 source .envrcenv -u CLOUDFLARE_API_TOKEN npx wrangler login # 환경변수 잠깐 빼고WSL에서도 localhost:8976 콜백이 정상 동작했다(예전 메모엔 안 된다고 돼 있었음)..envrc 등).로그인 관련 코드(arctic v3 기반 OAuth, 세션 관리, 미들웨어, 콜백 라우트, /login)는
이미 다 짜여 있었고 시크릿만 없어서 비활성인 상태였다. 그래서 "만들기"가 아니라
"활성화"만 했다.
https://ppnation.heohyuk.workers.devhttps://ppnation.heohyuk.workers.dev/api/auth/callback/githubnpx wrangler secret put GITHUB_CLIENT_ID
npx wrangler secret put GITHUB_CLIENT_SECRETwrangler secret put으로 넣는다.read -rs S && printf %s "$S" | npx wrangler secret put GITHUB_CLIENT_SECRET && unset S/login에서 GitHub 로그인. 첫 로그인 사용자가 자동 관리자.안 쓰기로 해서, login/account에서 isConfigured()로 미설정 공급자를 아예 숨겼다.
나중에 Google 시크릿만 등록하면 코드 변경 없이 다시 나타난다.
npx wrangler가 아니라 wrangler로 쳐서 "명령을 찾을 수 없음" → Windows CMD가 아니라npx 붙여 실행해야 함. wrangler 로그인도 WSL 쪽에 저장돼 있음.curl로 /api/account POST가 403 → Astro의 Origin(CSRF) 검사. 실제 브라우저 폼/account로그인 사용자가 자기 계정을 관리하는 페이지. users 테이블(id, email, name,
avatar_url, is_admin, created_at) + oauth_accounts(소셜 연결) 기준.
sessions 전체 삭제)src/lib/account.ts: listLinkedProviders, renameUser, deleteAllSessions,unlinkProvider, countAuthoredPosts, deleteAccountsrc/pages/api/account.ts: HTML 폼이라 intent 필드로 동작 분기rename | unlink | logout-all | delete)?ok= / ?err= 쿼리로 넘겨 페이지에서 문구 매핑/account 링크D1(SQLite)은 기본적으로 FK를 강제하지 않으므로 의존 순서대로 직접 지운다.
db.batch([...])로 묶어서:
DELETE FROM comments WHERE author_id = ?
DELETE FROM sessions WHERE user_id = ?
DELETE FROM oauth_accounts WHERE user_id = ?
DELETE FROM users WHERE id = ?
자료실 데이터를 사이트 코드와 섞지 않으려고 전용 퍼블릭 레포
heo-hyuk/ShareTemp로 옮겼다. 사이트 쪽은 상수 한 줄만 교체:
export const RESOURCES_REPO = 'heo-hyuk/ShareTemp';
raw.githubusercontent.com은 Content-Disposition: attachment를 안 붙인다.
그래서 타입별로 제각각이었다:
| 파일 | 브라우저 동작 |
|---|---|
.zip .xlsx .pptx | 그냥 다운로드 |
.pdf | 뷰어로 열림 |
.md .txt .csv | 화면에 텍스트로 표시 (Ctrl+S 해야 저장) |
<a download> 속성은 교차 출처면 무시되므로 해결책이 안 된다.
src/pages/api/download/[...path].ts:
..·절대경로 차단)raw.githubusercontent.com에서 fetchContent-Disposition: attachment 붙여 스트리밍 재전송filename*=UTF-8''<percent-encoded>) + ASCII 폴백cache-control: public, max-age=3600compatibility_flags에 global_fetch_strictly_public이 있어도 raw는 퍼블릭이라 OK.
한글 파일명(샘플-강의노트.txt)으로 검증:
content-disposition: attachment; filename="__-____.txt";
filename*=UTF-8''%EC%83%98%ED%94%8C-...txt
→ 브라우저에서 샘플-강의노트.txt로 저장됨. 경로 탈출/없는 파일은 400/404.
이 프로젝트는 Cloudflare Workers(Pages 아님)라 GitHub push로 자동 배포되지 않는다.
로컬에서 직접:
npm run deploy # = astro build && wrangler deploy
(멀티 계정 이슈로 한동안은 env -u CLOUDFLARE_API_TOKEN npm run deploy로 실행.
3번 정리 후 새 셸에서는 그냥 npm run deploy.)
8c3a506 chore(deps): 의존성 패치 업그레이드
c288533 feat(resources): 자료실을 GitHub 레포 매니페스트 기반으로 전환
d7173f1 docs(deploy): OAuth 로그인(asdf1378kk) 기준으로 갱신
07e245c feat(account): 내 정보/설정 페이지 (/account)
997f297 feat(auth): 미설정 소셜 공급자 숨김 (현재 GitHub만 노출)
0df8eb2 refactor(resources): 자료실 저장소를 heo-hyuk/ShareTemp 로 변경
f2a8f57 feat(resources): 파일 자료 강제 다운로드 프록시 라우트
<a download>도, raw URL도 강제 저장을