+ +
+로그인 확인 중
+ 로그아웃 +S/W 문의
소프트웨어 관련 문의하기
@@ -204,5 +213,5 @@diff --git a/README.md b/README.md index 2506dad..2dcdc8a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # EG-BIM Q&A static pages -`egbim/index.html`의 목록 위에 작성·상세 모달을 여는 EG-BIM Q&A UI입니다. `SW문의_소스패키지`의 히어로·제품 탭·목록·모달 디자인을 적용하고, 기존 첨부·댓글·비밀글·작성자 권한·검색을 유지합니다. 회사 홈페이지의 헤더·메뉴·푸터는 포함하지 않습니다. 기존 `write.html`, `detail.html?id=...` 주소는 같은 목록의 모달로 연결됩니다. +`egbim/index.html`의 목록 위에 작성·상세 모달을 여는 EG-BIM Q&A UI입니다. `SW문의_소스패키지`의 히어로·제품 탭·목록·모달 디자인을 적용하고, 기존 첨부·댓글·비밀글·작성자 권한·검색을 유지합니다. 회사 홈페이지의 헤더·메뉴·푸터는 포함하지 않습니다. 기존 `write.html`, `detail.html?id=...` 주소는 같은 목록의 모달로 연결됩니다. 상단 오른쪽 사람 아이콘의 계정 메뉴에서 로그인 사용자 이름을 확인하고 로그아웃할 수 있습니다. EG-BIM 탭만 가운데 표시하며 TOVA·GAIA는 오픈 전까지 숨깁니다. 원격지원은 `https://939.co.kr/saman/`으로 열립니다. UI 적용 범위·검증·남은 배포 확인은 [작업 목록](docs/qa-inquiry-ui-redesign-tasks.md)에 정리했습니다. @@ -78,6 +78,8 @@ npx wrangler secret bulk qa-secrets.json --name baron-qa-gateway-test Worker는 OAuth Authorization Code + PKCE를 사용하고, callback에서 검증한 사용자 claim을 서명된 HttpOnly 세션 쿠키에 저장합니다. 브라우저의 `GET /auth/session`은 정규화된 작성자 정보만 반환합니다. +로그아웃 메뉴는 `/auth/logout`으로 이동해 해당 환경의 Q&A 세션 쿠키를 삭제한 뒤 `https://test.baroncs.co.kr/ko/sw-inquiry.html`로 돌아갑니다. 스테이징·운영의 `AUTH_POST_LOGOUT_REDIRECT_URI`에 같은 목적지를 설정합니다. 목적지는 서버 배포 설정에서만 읽으며 요청 쿼리로 변경할 수 없습니다. 중앙 SSO의 로그인 종료 요청은 별도로 보내지 않습니다. + ## Gitea Actions 등록값 운영 `qa.baroncs.co.kr` 배포는 [production 파이프라인 안내](docs/qa-production-pipeline.md)를 따른다. 운영 workflow는 `Deploy EG-BIM QA Production`이며 `main`에서 수동으로 실행한다. 아래 기존 등록값은 스테이징 배포용이다. diff --git a/assets/inquiry/inquiry.css b/assets/inquiry/inquiry.css index c8f0878..b2e4b36 100644 --- a/assets/inquiry/inquiry.css +++ b/assets/inquiry/inquiry.css @@ -8,6 +8,17 @@ .inquiry-hero__copy { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; text-align: center; padding: 40px 24px; } .inquiry-hero h1 { font-size: 70px; font-weight: 700; line-height: 1.4; letter-spacing: -0.04em; } .inquiry-hero p { font-size: 22px; line-height: 1.6; } +.inquiry-account { position: absolute; top: 24px; right: 32px; z-index: 3; color: #fff; } +.inquiry-account summary { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border: 1px solid #ffffff99; border-radius: 50%; background: #0b274366; cursor: pointer; list-style: none; } +.inquiry-account summary::-webkit-details-marker { display: none; } +.inquiry-account summary:hover, .inquiry-account[open] summary { background: #0b2743b3; } +.inquiry-account summary:focus-visible { outline-color: #fff; } +.inquiry-account svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; } +.inquiry-account__panel { position: absolute; top: 54px; right: 0; width: 192px; padding: 8px; border: 1px solid #e5e9ef; border-radius: 10px; background: #fff; color: #26374f; box-shadow: 0 8px 28px #0003; } +.inquiry-hero .inquiry-account__panel p { overflow: hidden; padding: 10px 12px; color: #697386; font-size: 13px; white-space: nowrap; text-overflow: ellipsis; } +.inquiry-account__panel a { display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 10px 12px; border-radius: 6px; font-size: 14px; } +.inquiry-account__panel a:hover { background: #edf3fd; color: #164b91; } +.inquiry-account__panel a svg { width: 18px; height: 18px; } .inquiry-workspace { position: relative; z-index: 1; width: min(1248px, 100%); padding: 0 24px; margin: -98px auto 120px; } .inquiry-tabs-shell { --inquiry-tab-curve: 28px; padding: 19px 32px 0; border-radius: 48px 48px 0 0; background: linear-gradient(180deg, #174b64a6, #082f43d9); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: inset 0 1px 0 #74c7f4b3, inset 0 0 0 1px #5fadd666, inset 0 8px 16px #ffffff12, 0 -18px 12px #0a2f4843; } .inquiry-tabs { display: flex; justify-content: center; gap: 60px; position: relative; isolation: isolate; } @@ -116,6 +127,7 @@ html.inquiry-modal-open body { overflow: hidden !important; } .inquiry-hero { --inquiry-hero-shade: rgba(5, 24, 44, 0.38); height: 340px; background-position: 22% center; } .inquiry-hero h1 { font-size: 48px; } .inquiry-hero p { font-size: 17px; } + .inquiry-account { top: 16px; right: 16px; } .inquiry-workspace { margin: -96px auto 72px; padding: 0 16px; } .inquiry-tabs-shell { --inquiry-tab-curve: 13px; padding: 12px 13px 0; border-radius: 30px 30px 0 0; } .inquiry-tabs { gap: 5px; } diff --git a/docs/qa-inquiry-ui-redesign-tasks.md b/docs/qa-inquiry-ui-redesign-tasks.md index 1832b71..4cc3083 100644 --- a/docs/qa-inquiry-ui-redesign-tasks.md +++ b/docs/qa-inquiry-ui-redesign-tasks.md @@ -228,7 +228,7 @@ flowchart LR ## 10. 구현·검증 결과 -변경은 정적 HTML/CSS/JS와 자산 업로드 MIME·검증·문서에 한정했다. Worker·API 계약·DB·SSO 설정·기존 배포 트리거는 변경하지 않았다. 기존 주소는 프론트의 `assets/inquiry/entry.js`로 연결하며, 새 화면은 기존 API를 그대로 호출한다. 원본 패키지 전체를 서비스 자산으로 복사하지 않았다. +초기 목록·모달 UI 변경은 정적 HTML/CSS/JS와 자산 업로드 MIME·검증·문서에 한정했다. 해당 작업에서 Worker·API 계약·DB·SSO 설정·기존 배포 트리거는 변경하지 않았다. 기존 주소는 프론트의 `assets/inquiry/entry.js`로 연결하며, 새 화면은 기존 API를 그대로 호출한다. 원본 패키지 전체를 서비스 자산으로 복사하지 않았다. - `npm run check`, `npm test`: 기존 94개와 구형 주소 연결 4개, 총 98개 테스트 통과. - R2 업로드 검사: 새 하위 경로의 진입 JS와 WebP 이미지가 기존 배포 경로에 포함되며 MIME은 각각 `application/javascript`, `image/webp`다. @@ -254,4 +254,13 @@ CHROMIUM_EXECUTABLE_PATH=/absolute/path/to/chromium \ npm run test:ui ``` -기본 스크린샷 출력 위치는 임시 디렉토리의 `qa-inquiry-ui/`다. `UI_SCREENSHOT_DIR`로 바꿀 수 있으며, 데스크톱·모바일 목록/작성/상세 6장을 생성한다. 실제 사용자 세션·API Key는 필요하지 않다. +기본 스크린샷 출력 위치는 임시 디렉토리의 `qa-inquiry-ui/`다. `UI_SCREENSHOT_DIR`로 바꿀 수 있으며, 데스크톱·모바일 목록/작성/상세 6장과 모바일 계정 메뉴 화면을 생성한다. 실제 사용자 세션·API Key는 필요하지 않다. + + +## 11. 후속 계정 메뉴 + +- 상단 오른쪽 사람 아이콘으로 로그인 사용자 이름·로그아웃 메뉴를 연다. 바깥 클릭·ESC로 닫는다. +- 로그아웃은 기존 `/auth/logout`에서 Q&A 세션 쿠키를 삭제한 뒤 `https://test.baroncs.co.kr/ko/sw-inquiry.html`로 이동한다. +- 스테이징·운영의 `AUTH_POST_LOGOUT_REDIRECT_URI`를 위 주소로 설정했다. 서버 설정의 HTTPS 목적지로 이동할 수 있게 기존 로그아웃 처리를 조정했으며 로그인 `return_url` 정책은 유지한다. +- 중앙 SSO 세션 종료 API는 호출하지 않는다. 추가 Secret·SSO client/callback 변경은 없다. +- 검증: 스테이징/운영 세션 쿠키 삭제·고정 목적지·쿼리 덮어쓰기 방지·잘못된 설정 처리 테스트, 브라우저 메뉴/키보드/모바일 표시 및 실제 Worker 핸들러를 통한 쿠키 삭제·목적지 이동 요청을 확인한다. 리다이렉트 목적지로의 실제 외부 접속은 Chromium 네트워크 차단으로 막는다. 기본 테스트 총 106개와 브라우저 회귀 검사를 통과했다. diff --git a/docs/qa-login-to-save-transition.md b/docs/qa-login-to-save-transition.md index 01c29a5..92e76fa 100644 --- a/docs/qa-login-to-save-transition.md +++ b/docs/qa-login-to-save-transition.md @@ -1,6 +1,6 @@ # EG-BIM Q&A 로그인·화면·저장 흐름 -기준일: 2026-11-02. 스테이징 `qa-test.baroncs.co.kr`, 운영 `qa.baroncs.co.kr`의 현재 구조를 설명한다. 이번 UI 개편은 프론트에 한정하며 Worker API·DB·SSO 설정을 변경하지 않는다. +기준일: 2026-11-02. 스테이징 `qa-test.baroncs.co.kr`, 운영 `qa.baroncs.co.kr`의 현재 구조를 설명한다. 목록·모달 UI는 기존 API·DB·SSO 로그인 흐름을 유지한다. 후속 계정 메뉴 작업에서는 기존 로그아웃 경로의 이동 목적지를 홈페이지 문의 페이지로 연결했다. ## 로그인과 화면 진입 @@ -36,6 +36,10 @@ flowchart TD M -->|아니오| O[모달 오류 표시·입력과 파일 유지] ``` +## 계정 메뉴와 로그아웃 + +상단 오른쪽 사람 아이콘을 누르면 로그인 사용자 이름과 로그아웃 링크가 나온다. 바깥 클릭이나 ESC로 메뉴를 닫을 수 있다. `/auth/logout`은 현재 환경의 Q&A 세션 쿠키를 만료시키고 `https://test.baroncs.co.kr/ko/sw-inquiry.html`로 302 이동한다. 목적지는 스테이징·운영 Wrangler 설정의 `AUTH_POST_LOGOUT_REDIRECT_URI`에서 읽으며 사용자 쿼리로 덮어쓸 수 없다. 중앙 SSO 세션 종료 API는 호출하지 않는다. + ## 작성·첨부 저장 브라우저는 기존 UUID v7 생성 방식으로 `feedbackId`를 만들고 아래 필드를 보낸다. 회사·부서·작성자·이메일 칸은 읽기 전용이며, 서버가 기존 세션·조직 조회 결과에서 작성자 정보를 결정한다. diff --git a/docs/qa-production-deployment-checklist.md b/docs/qa-production-deployment-checklist.md index ccca734..714659c 100644 --- a/docs/qa-production-deployment-checklist.md +++ b/docs/qa-production-deployment-checklist.md @@ -23,7 +23,7 @@ ### 2. SSO 운영 등록 - [x] 운영 callback URI를 SSO에 등록했다: `https://qa.baroncs.co.kr/auth/callback`. 사용자 완료 보고 기준이다. -- [x] 로그아웃 후 이동 URI를 SSO에 등록했다: `https://qa.baroncs.co.kr/`. 사용자 완료 보고 기준이다. +- [x] 초기 SSO 등록의 로그아웃 후 URI는 `https://qa.baroncs.co.kr/`로 등록했다. 사용자 완료 보고 기준이다. 현재 계정 메뉴의 Q&A 로그아웃은 Worker에서 세션을 삭제하고 별도 설정된 홈페이지 문의 주소로 직접 이동한다. - [x] 운영 SSO 설정에 맞는 `PROD_AUTH_CLIENT_ID` 등 관련 변수 등록을 완료했다. 실제 로그인 동작 확인은 운영 배포 후 수행한다. - [ ] 운영 도메인에서 실제 사내 사용자로 로그인·로그아웃할 수 있는지 확인한다. @@ -56,7 +56,7 @@ | Worker 이름 | `baron-qa-gateway-prod` | | Worker custom domain | `qa.baroncs.co.kr` | | AUTH redirect URI | `https://qa.baroncs.co.kr/auth/callback` | -| AUTH post logout URI | `https://qa.baroncs.co.kr/` | +| Q&A 로그아웃 후 이동 URI | `https://test.baroncs.co.kr/ko/sw-inquiry.html` (`AUTH_POST_LOGOUT_REDIRECT_URI`) | | AUTH client ID | 운영 SSO client ID | | ABC API base URL | `https://feedback.hmac.kr` 또는 운영 API 주소 | | ABC project ID / channel ID | 운영 대상 ID | diff --git a/docs/qa-production-pipeline.md b/docs/qa-production-pipeline.md index a240470..23dc850 100644 --- a/docs/qa-production-pipeline.md +++ b/docs/qa-production-pipeline.md @@ -61,7 +61,7 @@ R2 권한은 계정 범위의 **Workers R2 Storage Write/Edit**를 선택한다. | `PROD_ORG_CONTEXT_EXPOSURE` | 선택 | 기본 `identifiers` | | `PROD_ORG_CONTEXT_CACHE_TTL_SECONDS` | 선택 | 기본 `300`, 허용 범위 1~3600 | -SSO에는 `https://qa.baroncs.co.kr/auth/callback`을 허용하고, 로그아웃 후 이동 URI로 `https://qa.baroncs.co.kr/`을 확인한다. 등록된 SSO client나 ABC project/channel이 운영 대상인지 확정한 후 배포한다. +SSO에는 `https://qa.baroncs.co.kr/auth/callback`을 허용한다. Q&A 로그아웃은 Worker가 세션 쿠키를 삭제한 뒤 `AUTH_POST_LOGOUT_REDIRECT_URI`에 설정된 `https://test.baroncs.co.kr/ko/sw-inquiry.html`로 이동한다. 이 이동은 중앙 SSO 로그아웃 API를 사용하지 않는다. 등록된 SSO client나 ABC project/channel이 운영 대상인지 확정한 후 배포한다. 지금 준비할 Variables는 `PROD_AUTH_CLIENT_ID`와 `PROD_ORG_CONTEXT_*` 6개다. `PROD_AUTH_CLIENT_ID`의 참고값은 기존 스테이징 client `e16898a7-acd2-420a-a224-cdcd89ff66cd`이며, 이 client에 운영 callback이 허용된 경우에만 운영에서도 재사용한다. 별도 운영 client를 만들었다면 그 ID를 등록한다. 기준 tenant UUID와 조직 연동 키는 실제 발급받은 값을 사용한다. 기존 `STAGING_*` 등록은 `PROD_*` 등록으로 간주하지 않는다. diff --git a/egbim/app.js b/egbim/app.js index a669933..709901a 100644 --- a/egbim/app.js +++ b/egbim/app.js @@ -501,6 +501,18 @@ console.error('[QA] SSO session load failed:', error); return getAuthUser(); }).then(function (user) { renderAuth(user); return user; }); + const accountMenu = qs('#accountMenu'); + if (accountMenu) { + document.addEventListener('click', function (event) { + if (!accountMenu.contains(event.target)) accountMenu.open = false; + }); + document.addEventListener('keydown', function (event) { + if (event.key === 'Escape' && accountMenu.open) { + accountMenu.open = false; + qs('#accountButton').focus(); + } + }); + } const menuButton = qs('.menu-button'); const nav = qs('.global-nav'); if (menuButton && nav) { diff --git a/egbim/index.html b/egbim/index.html index 90a943f..d758278 100644 --- a/egbim/index.html +++ b/egbim/index.html @@ -1,7 +1,16 @@ -
로그인 확인 중
+ 로그아웃 +소프트웨어 관련 문의하기
@@ -204,5 +213,5 @@