QPIDS / commit_notes.md
Hyungseoky's picture
Update commit_notes.md
dfb5283 verified
|
Raw
History Blame Contribute Delete
13.4 kB
# SNAP 인증 톡합 β€” 컀밋 λ©”μ‹œμ§€ 및 λ³€κ²½ λ‚΄μ—­
---
## 컀밋 λ©”μ‹œμ§€
```
feat: LDAP 인증 톡합 β€” 둜그인 κ²Œμ΄νŒ… + HttpOnly μΏ ν‚€ + UserMenu
SKμ‹€νŠΈλ‘  사내 AD(Active Directory) 기반 LDAP 인증을 SNAP μ›Ή UI에 톡합.
BFF(Express)κ°€ 인증 μ„œλ²„(10.150.6.47:8090)λ₯Ό ν”„λ‘μ‹œν•˜μ—¬
HttpOnly μΏ ν‚€λ‘œ JWTλ₯Ό κ΄€λ¦¬ν•˜λŠ” ꡬ쑰.
## μ£Όμš” λ³€κ²½
### server.ts β€” BFF 인증 라우트 μΆ”κ°€
- POST /api/auth/login: 인증 μ„œλ²„ ν”„λ‘μ‹œ + HttpOnly μΏ ν‚€ Set-Cookie
- POST /api/auth/logout: μΏ ν‚€ μ‚­μ œ + 인증 μ„œλ²„ λΈ”λž™λ¦¬μŠ€νŠΈ 등둝
- cookie-parser 미듀웨어 μΆ”κ°€
- AUTH_API_BASE ν™˜κ²½λ³€μˆ˜ (κΈ°λ³Έ: http://10.150.6.47:8090)
### src/App.tsx β€” 인증 UI μ»΄ν¬λ„ŒνŠΈ μΆ”κ°€ (1326쀄 β†’ 1657쀄)
- AuthProvider: μ „μ—­ 인증 μƒνƒœ (login/logout/user) + sessionStorage 동기화
- LoginScreen: 전체화면 둜그인 UI (μ‚¬λ²ˆ/λΉ„λ°€λ²ˆν˜Έ, SK λ””μžμΈ 토큰 적용)
- UserMenu: Navbar 우츑 μ‚¬μš©μž 아바타 + 이름 + λ“œλ‘­λ‹€μš΄(λΆ€μ„œ/직책/λ‘œκ·Έμ•„μ›ƒ)
- AuthGate: 둜그인 ν•„μˆ˜ κ²Œμ΄νŒ… (λΉ„λ‘œκ·ΈμΈ β†’ LoginScreen, 둜그인 β†’ AppContent)
- κΈ°μ‘΄ App() β†’ AppContent()둜 λ¦¬λ„€μž„, μƒˆ App()이 AuthProvider 래퍼
### src/types.ts β€” AuthUser μΈν„°νŽ˜μ΄μŠ€ μΆ”κ°€
- name, username, department, company, email, section, title ν•„λ“œ
- 인증 μ„œλ²„ /login 응닡 ꡬ쑰와 1:1 λ§€ν•‘
## 인증 흐름
1. μ‚¬μš©μž 접속 β†’ AuthGateκ°€ sessionStorage 확인
2. λΉ„λ‘œκ·ΈμΈ β†’ LoginScreen ν‘œμ‹œ
3. μ‚¬λ²ˆ/λΉ„λ°€λ²ˆν˜Έ μž…λ ₯ β†’ POST /api/auth/login
4. BFF β†’ 인증 μ„œλ²„(form-urlencoded) β†’ LDAP bind
5. 성곡 β†’ BFFκ°€ JWTλ₯Ό HttpOnly μΏ ν‚€λ‘œ set + μ‚¬μš©μž 정보λ₯Ό JSON λ°˜ν™˜
6. Reactκ°€ sessionStorage에 μ‚¬μš©μž 정보 μ €μž₯ β†’ AppContent ν‘œμ‹œ
7. λ‘œκ·Έμ•„μ›ƒ β†’ μΏ ν‚€ μ‚­μ œ + 인증 μ„œλ²„ λΈ”λž™λ¦¬μŠ€νŠΈ 등둝 β†’ LoginScreen 볡귀
## λ³΄μ•ˆ
- JWTλŠ” HttpOnly μΏ ν‚€λ‘œλ§Œ 보관 (XSS λ°©μ–΄)
- 토큰이 React μ½”λ“œμ— λ…ΈμΆœλ˜μ§€ μ•ŠμŒ
- 인증 μ„œλ²„ IPκ°€ ν΄λΌμ΄μ–ΈνŠΈμ— λ…ΈμΆœλ˜μ§€ μ•ŠμŒ (BFF ν”„λ‘μ‹œ)
- SameSite=Lax (CSRF κΈ°λ³Έ λ°©μ–΄)
- sessionStorage: νƒ­ μ’…λ£Œ μ‹œ μ„Έμ…˜ 만료
## μ˜μ‘΄μ„± μΆ”κ°€
- cookie-parser
- @types/cookie-parser (devDependencies)
## ν™˜κ²½λ³€μˆ˜ μΆ”κ°€
- AUTH_API_BASE: 인증 μ„œλ²„ URL (κΈ°λ³Έ: http://10.150.6.47:8090)
```
---
## λ³€κ²½ 파일 λͺ©λ‘
| 파일 | μƒνƒœ | λ³€κ²½ λ‚΄μš© |
|------|------|-----------|
| `server.ts` | Modified | cookie-parser + 인증 라우트 2개 μΆ”κ°€ |
| `src/App.tsx` | Modified | AuthProvider, LoginScreen, UserMenu, AuthGate μΆ”κ°€ |
| `src/types.ts` | Modified | AuthUser μΈν„°νŽ˜μ΄μŠ€ μΆ”κ°€ |
| `package.json` | Modified | cookie-parser μ˜μ‘΄μ„± μΆ”κ°€ |
---
## Git λͺ…λ Ή
```bash
# 1. νŒ¨ν‚€μ§€ μ„€μΉ˜ (이미 ν–ˆλ‹€λ©΄ μƒλž΅)
npm install cookie-parser
npm install -D @types/cookie-parser
# 2. μŠ€ν…Œμ΄μ§•
git add server.ts src/App.tsx src/types.ts package.json package-lock.json
# 3. 컀밋
git commit -m "feat: LDAP 인증 톡합 β€” 둜그인 κ²Œμ΄νŒ… + HttpOnly μΏ ν‚€ + UserMenu"
# 4. ν‘Έμ‹œ
git push origin main
```
---
## μ•„ν‚€ν…μ²˜ λ‹€μ΄μ–΄κ·Έλž¨
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Browser β”‚ β”‚ Express BFF β”‚ β”‚ Auth Server β”‚ β”‚ AD/LDAP β”‚
β”‚ (React) β”‚ β”‚ (server.ts) β”‚ β”‚ (Starlette) β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ LoginScreen │─────►│ /api/auth/ │─────►│ /login │─────►│ LDAP β”‚
β”‚ μ‚¬λ²ˆ+λΉ„λ²ˆ β”‚ POST β”‚ login β”‚ POST β”‚ β”‚ bind β”‚ bind β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ │◄─────│ Set-Cookie: │◄─────│ {user+token} │◄─────│ 인증결과 β”‚
β”‚ sessionStor β”‚ JSON β”‚ snap_auth=JWT β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ age μ €μž₯ β”‚(user)β”‚ (HttpOnly) β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ UserMenu β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ 이름+λΆ€μ„œ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ λ‘œκ·Έμ•„μ›ƒ │─────►│ /api/auth/ │─────►│ /logout β”‚ β”‚ β”‚
β”‚ β”‚ POST β”‚ logout β”‚ POST β”‚ Redis λΈ”λž™ β”‚ β”‚ β”‚
β”‚ │◄─────│ μΏ ν‚€ μ‚­μ œ β”‚ β”‚ 리슀트 등둝 β”‚ β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ 검색 μš”μ²­ │─────►│ /api/search │─────►│ β”‚ β”‚ β”‚
β”‚ (μΏ ν‚€ μžλ™) β”‚ β”‚ (κΈ°μ‘΄ 동일) β”‚ β”‚ qp-backend β”‚ β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```
---
## μ»΄ν¬λ„ŒνŠΈ ꡬ쑰 (App.tsx)
```
App (export default)
└─ AuthProvider (μ „μ—­ 인증 μƒνƒœ)
└─ AuthGate (κ²Œμ΄νŒ… λΆ„κΈ°)
β”œβ”€ isLoading β†’ μŠ€ν”Όλ„ˆ
β”œβ”€ !user β†’ LoginScreen (전체화면 둜그인)
└─ user β†’ AppContent (κΈ°μ‘΄ SNAP UI)
β”œβ”€ Navbar
β”‚ β”œβ”€ SNAP 둜고 + SONI/QMS 링크
β”‚ └─ UserMenu (μ‚¬μš©μž 아바타 + λ“œλ‘­λ‹€μš΄)
β”œβ”€ 검색 ν™”λ©΄ (view: 'search')
β”œβ”€ κ²°κ³Ό ν™”λ©΄ (view: 'results')
└─ DetailModal (상세 λͺ¨λ‹¬)
```
---
## ν™˜κ²½λ³€μˆ˜ 전체 λͺ©λ‘ (BFF)
| λ³€μˆ˜ | κΈ°λ³Έκ°’ | μ„€λͺ… |
|------|--------|------|
| `PORT` | `8888` | Express μ„œλ²„ 포트 |
| `QP_API_BASE` | `http://10.150.6.47:18503` | FastAPI λ°±μ—”λ“œ URL |
| `AUTH_API_BASE` | `http://10.150.6.47:8090` | LDAP 인증 μ„œλ²„ URL |
| `SNAP_DATA_DIR` | `../snap_data` | νŽ˜μ΄μ§€ 이미지 폴더 |
| `NODE_ENV` | (μ—†μŒ) | `production`이면 dist/ 정적 μ„œλΉ™ |
### μž‘μ—… 루트
seok@PSESL25717702:/mnt/d/dev/LLM/QP/service$ ls
README.md assets index.html metadata.json node_modules package-lock.json package.json server.ts src tsconfig.json vite.config.ts
-------------
# 배포 μ—λŸ¬
```bash
seok@PSESL25717702:/mnt/d/dev/LLM/QP/service$ npm run build
> react-example@0.0.0 build
> vite build
vite v6.4.2 building for production...
βœ“ 2072 modules transformed.
dist/index.html 0.55 kB β”‚ gzip: 0.40 kB
dist/assets/favicon-CIqKAyFz.ico 1.62 kB
dist/assets/index-BtKlGMUt.css 41.21 kB β”‚ gzip: 7.45 kB
dist/assets/index-D94KCa__.js 370.44 kB β”‚ gzip: 114.85 kB
βœ“ built in 46.73s
seok@PSESL25717702:/mnt/d/dev/LLM/QP/service$ npm start
> react-example@0.0.0 start
> node server.ts
node:internal/modules/esm/get_format:219
throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath);
^
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for D:\dev\LLM\QP\service\server.ts
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:219:9)
at defaultGetFormat (node:internal/modules/esm/get_format:245:36)
at defaultLoad (node:internal/modules/esm/load:120:22)
at async ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:580:32)
at async ModuleJob._link (node:internal/modules/esm/module_job:116:19) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
Node.js v22.16.0
seok@PSESL25717702:/mnt/d/dev/LLM/QP/service$
```
## why??
```bash
h200_1_user@dgx-h200:/home/Projects/projects/qp/images$ docker ps -a --filter "name=snap"
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
045ccb3438cb snap:1.0 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes (unhealthy) 0.0.0.0:18504->18505/tcp, [::]:18504->18505/tcp snap
h200_1_user@dgx-h200:/home/Projects/projects/qp/images$ docker logs --tail 200 snap
[snap_data] serving from /data/snap_data
Server running on http://localhost:18505
[auth] Auth server: http://10.150.6.47:8090
[logs] Activity logs: /data/logs
h200_1_user@dgx-h200:/home/Projects/projects/qp/images$ docker inspect snap | grep -A 30 "Health"
"Health": {
"Status": "unhealthy",
"FailingStreak": 9,
"Log": [
{
"Start": "2026-05-07T17:57:37.139147043+09:00",
"End": "2026-05-07T17:57:37.251677715+09:00",
"ExitCode": 1,
"Output": "wget: can't connect to remote host: Connection refused\n"
},
{
"Start": "2026-05-07T17:58:07.25268173+09:00",
"End": "2026-05-07T17:58:07.298976582+09:00",
"ExitCode": 1,
"Output": "wget: can't connect to remote host: Connection refused\n"
},
{
"Start": "2026-05-07T17:58:37.299534102+09:00",
"End": "2026-05-07T17:58:37.344486575+09:00",
"ExitCode": 1,
"Output": "wget: can't connect to remote host: Connection refused\n"
},
{
"Start": "2026-05-07T17:59:07.345563587+09:00",
"End": "2026-05-07T17:59:07.390899772+09:00",
"ExitCode": 1,
"Output": "wget: can't connect to remote host: Connection refused\n"
},
{
"Start": "2026-05-07T17:59:37.392160333+09:00",
"End": "2026-05-07T17:59:37.43758568+09:00",
--
"Healthcheck": {
"Test": [
"CMD-SHELL",
"wget -qO- http://localhost:18505/ > /dev/null || exit 1"
],
"Interval": 30000000000,
"Timeout": 5000000000,
"StartPeriod": 15000000000,
"Retries": 3
},
"Image": "snap:1.0",
"Volumes": null,
"WorkingDir": "/app",
"Entrypoint": [
"docker-entrypoint.sh"
],
"Labels": {}
},
"NetworkSettings": {
"SandboxID": "074deb5cc38a95e97f0cd8eb91353d72f0db1fd10fdd602673c55e6423e86abc",
"SandboxKey": "/var/run/docker/netns/074deb5cc38a",
"Ports": {
"18505/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "18504"
},
{
"HostIp": "::",
"HostPort": "18504"
}
```
---
```
# ─────────────────────────────────────────────
# SNAP Frontend + BFF (React + Express)
# Multi-stage: Vite λΉŒλ“œ β†’ κ²½λŸ‰ λŸ°νƒ€μž„
#
# λ³€κ²½ 이λ ₯:
# - LDAP 인증 톡합 (cookie-parser, /api/auth/*)
# - ν™œλ™ 둜그 μ‹œμŠ€ν…œ (/data/logs 마운트 ꢌμž₯)
# ─────────────────────────────────────────────
# ─── Build stage ───
FROM node:20-alpine AS builder
WORKDIR /app
# μ˜μ‘΄μ„± λ¨Όμ € μ„€μΉ˜ (도컀 λ ˆμ΄μ–΄ 캐싱)
COPY package.json package-lock.json ./
RUN npm ci
# μ†ŒμŠ€ 전체 볡사 β†’ Vite λΉŒλ“œ
# (App.tsx, main.tsx, index.css, types.ts, searchPipeline.ts λͺ¨λ‘ ν•„μš”)
COPY . .
RUN npm run build
# β†’ /app/dist 에 React λΉŒλ“œ κ²°κ³Ό (HTML/JS/CSS λ²ˆλ“€)
# ─── Runtime stage ───
FROM node:20-alpine
WORKDIR /app
# λŸ°νƒ€μž„ μ˜μ‘΄μ„±λ§Œ μ„€μΉ˜ (devDependencies μ œμ™Έ)
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && \
npm install --no-save tsx@^4.21.0 && \
npm cache clean --force
# Vite λΉŒλ“œ κ²°κ³Ό (HTML/JS/CSS λ²ˆλ“€)
COPY --from=builder /app/dist ./dist
# BFF μ„œλ²„ μ½”λ“œ + λŸ°νƒ€μž„ import λŒ€μƒ
# server.tsκ°€ src/services/searchPipeline.ts, src/types.tsλ₯Ό import 함
COPY server.ts ./
COPY src ./src
COPY tsconfig.json ./
# ν™œλ™ 둜그 디렉토리 생성 (ν˜ΈμŠ€νŠΈμ—μ„œ -v둜 λ§ˆμš΄νŠΈν•  μœ„μΉ˜)
# 마운트 μ•ˆ ν•˜λ©΄ μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€ μž„μ‹œ 폴더에 기둝됨 (μž¬μ‹œμž‘ μ‹œ μ†Œμ‹€)
RUN mkdir -p /data/logs && chmod 755 /data/logs
# ─── ν™˜κ²½λ³€μˆ˜ (λŸ°νƒ€μž„μ— docker run --env-file둜 μ£Όμž…) ───
ENV NODE_ENV=production
ENV PORT=18505
EXPOSE 18505
# Healthcheck β€” 30μ΄ˆλ§ˆλ‹€ / 응닡 확인
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD wget -qO- http://localhost:18505/ > /dev/null || exit 1
CMD ["npx", "tsx", "server.ts"]
```