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 λͺ…λ Ή

# 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


배포 μ—λŸ¬

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??

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"]