Google 계정 로그인 연동 추가
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
# 업데이트 요약
|
||||
|
||||
## v1.12.1
|
||||
|
||||
- Google 계정 로그인과 기존 계정 연결 흐름을 추가했다.
|
||||
- 관리자는 기존 관리자 계정에서 Google 계정을 연결한 경우에만 Google로 관리자 로그인할 수 있게 했다.
|
||||
|
||||
## v1.12.0
|
||||
|
||||
- v1.11 개발 구간의 글쓰기·미디어·가져오기 안정화와 내부 정리를 묶어 다음 minor 기준점으로 정리했다.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 배포 가이드
|
||||
|
||||
> v1.11.0은 v1.10 개발 구간을 마감한 minor 릴리스다. 운영 배포 전 `068_csp_violation_aggregates.sql`, `069_media_catalog.sql`, `070_post_import_jobs.sql`을 순서대로 적용하고 미디어 카탈로그는 reconcile 완료 후 `MEDIA_CATALOG_MODE=database` 고정 순서를 지킨다. 게시물 Export·Import·일반 업로드는 디스크·임시 디렉터리 스트리밍을 사용하므로 NAS 앱 컨테이너의 임시 저장 공간과 `public/uploads/exports`, `public/uploads/_import-jobs` 쓰기 권한을 확인한다.
|
||||
> 로컬 기준 v1.12.0에서 `npm run lint`, `npm test`, `npm run build`, `npm audit` 검증을 통과했다.
|
||||
> v1.12.1은 Google 로그인/계정 연동을 추가한다. 운영 배포 전 `068_csp_violation_aggregates.sql`, `069_media_catalog.sql`, `070_post_import_jobs.sql`, `071_users_google_oauth.sql`을 순서대로 적용하고 미디어 카탈로그는 reconcile 완료 후 `MEDIA_CATALOG_MODE=database` 고정 순서를 지킨다. 게시물 Export·Import·일반 업로드는 디스크·임시 디렉터리 스트리밍을 사용하므로 NAS 앱 컨테이너의 임시 저장 공간과 `public/uploads/exports`, `public/uploads/_import-jobs` 쓰기 권한을 확인한다.
|
||||
> 로컬 기준 v1.12.1에서 `npm run lint`, `npm test`, `npm run build`, `npm audit` 검증을 통과했다.
|
||||
|
||||
## 빌드 유형
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
> `npm run dev`는 프로젝트 전용 실행 스크립트를 통해 개발 서버, Admin, Tailwind Viewer 링크만 요약 출력한다.
|
||||
|
||||
## v1.11.0 배포 전 검증
|
||||
## v1.12.1 배포 전 검증
|
||||
|
||||
Playwright API 테스트도 브라우저 바이너리는 필요하지 않지만 `verify:ci`에는 Chromium E2E가 포함된다. 최초 1회 의존성과 Chromium을 설치하고 Docker daemon이 실행 중인지 확인한다.
|
||||
|
||||
@@ -40,6 +40,8 @@ sh scripts/migrate-production-db.sh status
|
||||
|
||||
- `068_csp_violation_aggregates.sql`은 CSP 보고 집계 테이블을 만든다.
|
||||
- `069_media_catalog.sql`은 미디어 카탈로그·참조·상태 계약을 만든다. 최초 상태는 `ready=false`이므로 마이그레이션 직후 곧바로 `MEDIA_CATALOG_MODE=database`만 켠 채 운영하지 않는다. 반드시 reconcile을 먼저 끝낸다.
|
||||
- `070_post_import_jobs.sql`은 게시물 Import 작업/항목 상태 테이블을 만든다.
|
||||
- `071_users_google_oauth.sql`은 회원 Google 로그인·계정 연동용 식별자 컬럼과 고유 인덱스를 만든다.
|
||||
- 배포 직후(또는 `filesystem`) 상태에서 관리자 인증 후 `POST /admin/api/media/reconcile`을 1회 실행한다. 이 API는 파일을 삭제하거나 이동하지 않고 발견 파일 upsert, 참조 백필, 사라진 파일 `missing` 표시만 수행한다.
|
||||
- `media_catalog_state`의 `ready=true`, 파일시스템/카탈로그 `count`와 `bytes` 일치, `staging`·`error` 행 0을 확인한다.
|
||||
- 확인이 끝나면 운영 env를 `MEDIA_CATALOG_MODE=database`로 고정하고 재기동한다. 개발 환경도 동일하게 `database`로 고정한다. 준비 미완료·불일치·DB 조회 오류가 발생하면 애플리케이션은 자동으로 파일시스템 목록으로 fallback한다.
|
||||
@@ -169,6 +171,8 @@ cp .env.example .env.production
|
||||
# .env.production 파일에 운영 DB 연결 정보, 운영 전용 랜덤 비밀번호, APP_PORT=43080, DB_PORT=43081 입력
|
||||
# 운영 DB에 owner/admin이 없으면 /admin/login에서 ADMIN_EMAIL/ADMIN_PASSWORD로 최초 owner 계정이 생성됨
|
||||
# MEMBER_SESSION_SECRET은 ADMIN_PASSWORD와 다른 긴 난수 문자열로 반드시 입력
|
||||
# Google 로그인을 사용하려면 GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET,
|
||||
# GOOGLE_OAUTH_REDIRECT_URI=https://sori.studio/api/auth/google/callback 입력
|
||||
# AdSense 통계를 대시보드에 연동하려면 GOOGLE_ADSENSE_CLIENT_ID, GOOGLE_ADSENSE_CLIENT_SECRET,
|
||||
# GOOGLE_ADSENSE_REFRESH_TOKEN, GOOGLE_ADSENSE_ACCOUNT_NAME, GOOGLE_ADSENSE_CURRENCY_CODE를 추가로 입력
|
||||
# Docker 네트워크 대역이 NAS 기존 컨테이너와 겹치면 DOCKER_SUBNET을 다른 사설 대역으로 변경
|
||||
@@ -226,6 +230,23 @@ docker compose --env-file .env.production up -d
|
||||
|
||||
코드 변경 없이 `.env.production`만 수정했다면 `--build` 없이 `up -d`만으로 충분하다.
|
||||
|
||||
### Google 로그인 설정
|
||||
|
||||
Google 계정 로그인과 계정 연동을 사용하려면 Google Cloud Console의 OAuth 클라이언트에 아래 리다이렉트 URI를 등록한다.
|
||||
|
||||
- 로컬 개발: `http://127.0.0.1:43080/api/auth/google/callback`
|
||||
- 운영: `https://sori.studio/api/auth/google/callback`
|
||||
|
||||
운영 `.env.production`에는 아래 값을 추가한다. AdSense와 같은 OAuth 클라이언트를 써도 되지만, 장기적으로는 로그인용 클라이언트를 별도로 두는 편이 관리하기 쉽다.
|
||||
|
||||
```bash
|
||||
GOOGLE_OAUTH_CLIENT_ID=...
|
||||
GOOGLE_OAUTH_CLIENT_SECRET=...
|
||||
GOOGLE_OAUTH_REDIRECT_URI=https://sori.studio/api/auth/google/callback
|
||||
```
|
||||
|
||||
관리자 Google 로그인은 자동으로 열리지 않는다. 먼저 기존 관리자 이메일/비밀번호로 로그인한 뒤 `/settings`에서 Google 계정을 연결해야 하며, 연결된 `owner` 또는 `admin` 계정만 `/admin/login`의 Google 로그인을 사용할 수 있다.
|
||||
|
||||
### AdSense refresh token 발급
|
||||
|
||||
AdSense Management API를 관리자 대시보드에서 조회하려면 OAuth 클라이언트 ID/시크릿 외에 1회 발급한 refresh token이 필요하다.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 2026-07-29 v1.12.1 — Google 로그인은 기존 계정 연결 방식으로 추가한다
|
||||
|
||||
Google 계정은 사이트 계정을 대체하지 않고 외부 식별자(`sub`)로 연결한다. 일반 회원은 Google 로그인 시 연결된 계정이 있으면 그 계정으로 로그인하고, 없으면 이메일 중복이 없을 때 사이트 전용 회원을 새로 만든다. 관리자는 보안상 자동 생성이나 이메일 매칭을 허용하지 않고, 기존 관리자 계정으로 먼저 로그인한 뒤 설정 화면에서 직접 Google 계정을 연결한 경우에만 관리자 Google 로그인을 허용한다.
|
||||
|
||||
## 2026-07-29 v1.12.0 — v1.11 내부 개선 구간을 마감한다
|
||||
|
||||
v1.11에서는 에디터·미디어·Import/Export·보안 의존성처럼 패치 단위 개선이 길게 이어졌다. 상세 작업 이력을 한 문서에 계속 누적하면 최신 상태 확인보다 과거 항목 검색 비용이 커지므로, `docs/update.md`는 v1.11 이후의 최신 이력 중심으로 유지하고 v1.10 이하 상세 기록은 `docs/update-archive.md`로 분리한다. 방문자에게 공유할 변화는 v1.11 개발 구간을 묶어 `docs/update-log/v1.12.md`로 별도 정리한다.
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
| lib/analytics-traffic.js | referrer·User-Agent 기반 유입원·디바이스·검색 키워드 축약 분류 |
|
||||
| lib/admin-pagination.js | 관리자 번호형 페이지네이션 페이지 보정·ellipsis 항목 계산 |
|
||||
| lib/csp-report.js | CSP 보고 Content-Type·32KB/20건 제한 상수, payload 검증, 문서 경로·차단 origin·브라우저 계열 최소화 |
|
||||
| server/utils/google-oauth.js | Google 로그인·계정 연동 OAuth 설정, state 서명·검증, 토큰 교환, 내부 redirect 보정 |
|
||||
| lib/site-route-transition.js | 공개 라우트 전환 베일 사용 여부와 query/hash 이동 제외, 스크롤 복원·최상단 이동 helper |
|
||||
| lib/media-catalog.js | 업로드 URL·저장 키 정규화, 미디어 종류·원본/파생 역할 판별, 콘텐츠 미디어 참조 추출, PostgreSQL LIKE 리터럴 이스케이프 |
|
||||
| lib/social-links.js | 사이트 설정 SNS 링크 아이콘 프리셋·사용자 SVG 정리·URL 자동 보정·레거시 JSON 문자열 복구·공개 노출 목록 생성 |
|
||||
@@ -393,9 +394,9 @@
|
||||
| pages/tag/[slug].vue | 태그별 글 목록, 존재하지 않는 태그 404, 사이트 설정 태그 게시물 표시개수 기준 서버 태그 필터·공용 번호형 페이지네이션, 태그 헤더 이미지가 있으면 상단 이미지 헤더 표시, 없으면 기본 태그명·설명 + 공통 섹션 패딩을 쓰는 리스트형 게시물 카드, 카드 태그 배지와 오른쪽 아래 화살표 링크 |
|
||||
| pages/pages/[slug].vue | 고정 페이지 상세, HTML 문서 모드는 직접 진입 시 서버 미들웨어 원문 응답·클라이언트 내부 이동 시 현재 URL reload로 서버 원문 응답 재진입 |
|
||||
| pages/signup.vue | 회원가입 3단계, `emailOtpConfigured`일 때 이메일 OTP·인증번호 받기, 모바일 OTP 입력창 높이 보정, `POST /api/auth/email-otp/request` |
|
||||
| pages/signin.vue | 로그인, `/forgot-password` 링크 |
|
||||
| pages/signin.vue | 로그인, Google 로그인, `/forgot-password` 링크 |
|
||||
| pages/forgot-password.vue | 비밀번호 찾기(Resend 설정 시 OTP 발송·`POST /api/auth/password-reset/confirm`) |
|
||||
| pages/settings/index.vue | 회원 설정(상단 프로필 요약, 가입 정보, 댓글 참여도, 하단 프로필 입력·이전 로그인 활동, 썸네일 변경·제거, 닉네임 저장, 설정 메뉴 모달 비밀번호 변경·회원 탈퇴) |
|
||||
| pages/settings/index.vue | 회원 설정(상단 프로필 요약, 가입 정보, 댓글 참여도, 하단 프로필 입력·이전 로그인 활동, Google 계정 연결 상태·연결 버튼, 썸네일 변경·제거, 닉네임 저장, 설정 메뉴 모달 비밀번호 변경·회원 탈퇴) |
|
||||
|
||||
## 서버 API
|
||||
|
||||
@@ -415,6 +416,8 @@
|
||||
| server/routes/admin/api/posts/[id]/featured.put.js | 관리자 글 목록 추천 상태 전용 수정 API |
|
||||
| server/api/navigation.get.js | 공개 네비게이션 API |
|
||||
| server/api/auth/signup.post.js | 회원 가입 API(선택 `emailOtp`, Resend 설정 시 일반 가입에 필수) |
|
||||
| server/api/auth/google/start.get.js | Google 로그인·계정 연동 OAuth 시작 API |
|
||||
| server/api/auth/google/callback.get.js | Google OAuth callback, 회원/관리자 세션 발급 또는 기존 계정 연결 |
|
||||
| server/api/auth/bootstrap-status.get.js | 최초 관리자 여부 + `emailOtpConfigured` |
|
||||
| server/api/auth/email-otp/request.post.js | Resend로 OTP 발송(`signup` / `password_reset`), 발송 실패 챌린지 삭제 |
|
||||
| server/api/auth/password-reset/confirm.post.js | OTP 검증 후 비밀번호 재설정 |
|
||||
|
||||
@@ -212,6 +212,11 @@
|
||||
- 인증 화면 상태 메시지는 오류/안내를 분리해 `aria-live`로 노출한다.
|
||||
- 회원가입 1단계의 타이틀/설명은 `GET /api/site-settings`의 `title`, `description` 값을 우선 사용한다.
|
||||
- 회원 세션 쿠키 서명에는 `MEMBER_SESSION_SECRET`만 사용하며, 관리자 비밀번호를 fallback으로 쓰지 않는다.
|
||||
- Google 로그인은 `/api/auth/google/start`에서 시작하고 `/api/auth/google/callback`에서 처리한다. 서버는 `GOOGLE_OAUTH_CLIENT_ID`, `GOOGLE_OAUTH_CLIENT_SECRET`, 선택값 `GOOGLE_OAUTH_REDIRECT_URI`를 사용하며, 값이 없으면 기존 AdSense OAuth 클라이언트 값(`GOOGLE_ADSENSE_CLIENT_ID`, `GOOGLE_ADSENSE_CLIENT_SECRET`)을 fallback으로 사용할 수 있다.
|
||||
- Google OAuth state는 `MEMBER_SESSION_SECRET` 기반 HMAC 서명과 10분 nonce 쿠키로 검증한다. redirect는 `/`로 시작하는 내부 경로만 허용한다.
|
||||
- 일반 회원 Google 로그인은 `google_sub`로 기존 회원을 찾고, 없으면 이메일 중복이 없을 때 새 회원을 생성한다. 이미 같은 이메일로 가입된 계정이 있으면 기존 계정 로그인 후 Google 계정 연결을 요청한다.
|
||||
- 관리자 Google 로그인은 이미 `google_sub`가 연결된 `owner` 또는 `admin` 계정만 허용한다. 관리자 계정은 먼저 기존 비밀번호 로그인으로 들어간 뒤 회원 설정 화면에서 Google 계정을 연결해야 한다.
|
||||
- 회원 설정 화면은 Google 계정 연결 상태를 표시하고 `/api/auth/google/start?mode=link`로 현재 로그인 계정에 Google 식별자를 연결한다. 같은 Google 계정이 다른 회원에 이미 연결되어 있으면 연결하지 않는다.
|
||||
|
||||
### 레이아웃 파일
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v1.12.1
|
||||
|
||||
- Google 계정 로그인과 기존 계정 Google 연동 흐름 추가.
|
||||
- `users` 테이블에 Google OAuth 식별자·이메일·이름·프로필 이미지·연결 시각 컬럼을 추가하는 `071_users_google_oauth.sql` 마이그레이션 추가.
|
||||
- 일반 로그인 화면과 관리자 로그인 화면에 Google 로그인 버튼 추가.
|
||||
- 회원 설정 화면에 Google 계정 연결 상태와 연결 버튼 추가.
|
||||
- Google OAuth redirect 내부 경로 보정 테스트 추가.
|
||||
- Google OAuth 환경 변수와 redirect URI 등록 절차 문서화.
|
||||
- 로컬 개발 DB에 `071_users_google_oauth.sql` 마이그레이션 적용 확인.
|
||||
|
||||
## v1.12.0
|
||||
|
||||
- v1.11 내부 개선 구간을 마감하고 관리 버전을 `1.12.0`으로 상향.
|
||||
|
||||
Reference in New Issue
Block a user