바이브 빌드 (Vibe Build)

혼자서 서버구축, 바이브 코딩, WebUI 제작, 배포, 운영, 응용과정을 기록하는 기술 블로그 입니다.

5단계: 세션과 보호된 관리자 경로 만들기

5단계: 세션과 보호된 관리자 경로 만들기

Article Guide

목  차

핵심 요약

4단계까지는 MySQL로 로그인해도 새로고침하면 로그인 상태가 사라집니다. 이번 단계에서는 서버 측 세션과 HttpOnly 쿠키를 사용해 새로고침해도 로그인 상태가 유지되도록 합니다.

로그인 성공
  ↓
Express가 세션 ID 재발급
  ↓
세션에 사용자 ID 저장
  ↓
웹 브라우저에 HttpOnly 쿠키 전달
  ↓
새로고침
  ↓
GET /api/auth/me
  ↓
MySQL 사용자와 관리자 역할 재확인
  ↓
`Admin Dashboard` 유지

프런트엔드 경로 보호는 로그인하지 않은 사용자가 관리자 화면으로 이동하지 못하게 하는 기능입니다. 하지만 실제 보안 검사는 백엔드의 관리자 권한 미들웨어에서 처리합니다.

로그인 성공 후 세션 ID와 HttpOnly 쿠키를 발급하고 새로고침 때 관리자 대시보드를 복원하는 흐름

이번 단계의 Vibe Coding 작업 지시서

학습용 간단한 지시 설명서

04단계 MySQL 관리자 로그인에 express-session과 보호 경로를 추가해 줘.
로그인 성공 시 세션 ID를 재생성하고 userId만 저장하며 /api/auth/me와 /api/auth/logout을 구현해 줘.
백엔드 requireAdmin이 현재 MySQL 사용자의 활성 상태와 관리자 권한을 매번 확인하게 해 줘.
개발 단계는 MemoryStore를 사용하되 운영용이 아님을 명시하고 Redis는 아직 추가하지 마.
변경 전·후 Gate와 새로고침, 로그아웃, 비로그인·일반 사용자 접근 결과를 보고해 줘.

정식 작업 지시서

다음 코드 블록 전체가 05단계 구현의 유일한 정식 작업 지시서입니다. 내용을 줄이거나 별도의 상세 프롬프트로 복제하지 않고 docs/work-orders/05-session-protected-admin-route.md에 저장합니다.

# 05단계 정식 작업 지시서

## 단계별 상세 지시
루트 AGENTS.md, 요구사항과 docs/work-orders/05-session-protected-admin-route.md를 먼저 읽어 줘.
04단계 완료 확인표와 공통 WebUI Gate 통과를 확인하고 실패 상태면 구현하지 않는다.

허용 범위는 세션과 보호 경로에 필요한 backend/, frontend/와 환경 변수 예시다.
기존 로그인 API의 성공·실패 계약과 MySQL 사용자 검증을 보존한다.
변경 전에 세션 정책, 쿠키 설정, 변경 파일과 검증 명령을 보고한다.

개발 단계에서는 express-session의 MemoryStore를 사용하고 운영용이 아님을 코드와 문서에 명시한다.
쿠키 이름은 vibe.sid로 하고 httpOnly와 sameSite=lax를 적용한다.
HTTP 개발 환경에서는 secure=false를 사용하고 비밀값은 환경 변수로 받는다.
로그인 성공 후 기존 세션 ID를 regenerate하고 세션에는 userId만 저장한다.
GET /api/auth/me와 POST /api/auth/logout을 구현한다.
requireAdmin 미들웨어는 요청마다 MySQL 사용자의 존재, is_active와 role을 다시 확인한다.
프런트엔드에 Auth Context, 초기 세션 확인 상태와 보호된 /admin 경로를 구현한다.
프런트엔드 경로 검사만으로 보호 API 보안을 대체하지 않는다.
로그아웃은 쿠키뿐 아니라 서버 세션도 삭제한다.

Redis, Nginx와 운영 쿠키 설정은 추가하지 않는다.
`SESSION_SECRET`과 운영 환경의 세션 ID·쿠키 값은 로그, 문서와 화면 캡처에 기록하지 않는다. 이 문서의 로컬 연습용 일회성 쿠키 값은 동작 확인 예외로 허용한다.

변경 전·후 Gate를 실행하고 백엔드 typecheck와 프런트엔드 build를 확인한다.
로그인 후 새로고침, /api/auth/me, 로그아웃, 비로그인 직접 접근, 비활성·일반 사용자와 세션 ID 재생성을 검증한다.
검사 결과는 완료 보고에 통과, 실패와 미수행으로 구분한다. 검증 기록 문서 갱신은 사용자가 WebUI 확인을 마친 뒤 별도 프롬프트로 수행하므로 이 작업에서는 실행하지 않는다.
검사가 실패하면 06단계 Dashboard UI 확장을 시작하지 않는다.

## 완료 보고 형식

- 생성하거나 변경한 파일
- 실행한 명령과 실제 결과
- 통과, 실패와 미수행 검증
- 요구사항별 구현 내용과 변경 이유
- 남은 제한, 위험과 다음 단계 진행 가능 여부

정식 작업 지시서 저장과 실행 순서

다음 순서는 Windows 관리 PC의 VS Code에서 Remote SSH로 개발 서버에 연결한 상태를 기준으로 합니다. 앞 단계가 실패했거나 기존 변경과 충돌하면 다음 번호로 넘어가지 않습니다.

1. 프로젝트와 이전 단계 상태 확인

VS Code의 원격 터미널에서 프로젝트 루트로 이동하고 현재 상태를 확인합니다.

cd /home/apple2ne1/projects/vibe-coding-platform
pwd
git status --short
VS Code 원격 터미널에서 05단계 프로젝트 경로와 Git 작업 트리 상태를 확인한 화면

AI 코딩 에이전트에게 변경을 맡기기 전에 이전 단계의 완료 기록과 현재 사용자 변경을 구분합니다. 예상하지 못한 변경이 있으면 보존 방법을 먼저 결정합니다.

2. 정식 작업 지시서 저장

VS Code에서 docs/work-orders/05-session-protected-admin-route.md를 만들고, 위 정식 작업 지시서 코드 블록의 내용을 빠짐없이 저장합니다. 간단한 지시서와 정식 작업 지시서를 서로 다른 실행 Prompt로 보내지 않습니다.

방법 1: VS Code 작업

VS Code의 Explorer에서 docs/work-orders 폴더를 마우스 오른쪽 버튼으로 클릭하고 New File을 선택합니다.

VS Code Explorer에서 work-orders 폴더의 바로 가기 메뉴를 열고 New File을 선택하는 화면

파일 이름으로 05-session-protected-admin-route.md를 입력하고 Enter를 누릅니다.

VS Code Explorer에서 05단계 정식 작업 지시서 파일명을 입력하는 화면

방법 2: 터미널 작업

mkdir -p docs/work-orders
touch docs/work-orders/05-session-protected-admin-route.md

VS Code의 Explorer에서 docs/work-orders/05-session-protected-admin-route.md를 선택해 열고, 위 정식 작업 지시서 코드 블록 전체를 복사해 붙여넣은 뒤 Ctrl+S로 저장합니다.

VS Code 편집기에서 05단계 정식 작업 지시서 내용을 붙여넣고 저장한 화면

저장한 다음 VS Code 원격 터미널에서 파일 내용과 변경 사항을 확인합니다.

test -s docs/work-orders/05-session-protected-admin-route.md
git diff -- docs/work-orders/05-session-protected-admin-route.md
VS Code 원격 터미널에서 05단계 작업 지시서의 저장 여부와 Git 차이를 확인한 화면
  • 파일 존재여부 확인
  • 저장 후 파일의 변경여부 확인

저장한 파일에 목표, 허용 범위, 제외 범위, 완료 조건, 검증과 중단 조건이 모두 포함되어 있는지 직접 확인합니다.

3. 작업 지시서 Git 기준점 기록

정식 작업 지시서 코드 블록 전체를 지정한 파일에 저장한 뒤, 해당 파일만 먼저 커밋하여 구현 기준을 고정합니다.

cd /home/apple2ne1/projects/vibe-coding-platform
git status --short
test -s docs/work-orders/05-session-protected-admin-route.md
git add -- docs/work-orders/05-session-protected-admin-route.md
git diff --cached --name-only
git diff --cached --check
git commit -m "docs: add 05 step work order"
VS Code 원격 터미널에서 05단계 작업 지시서만 스테이징하고 파일 목록과 형식 검사를 확인한 화면

스테이징 파일 목록에 docs/work-orders/05-session-protected-admin-route.md 하나만 표시되고 git diff --cached --check가 오류 없이 종료되면 기준점 커밋을 진행합니다. 다른 파일이 표시되거나 검사가 실패하면 커밋하지 않고 Codex에 표시된 파일과 오류를 전달해 수정을 요청합니다.

4. Codex CLI 실행과 공통 프롬프트 전달

프로젝트 루트에서 Codex CLI를 실행합니다.

codex -C /home/apple2ne1/projects/vibe-coding-platform

Codex CLI에 단계별 구현 조건을 다시 길게 복사하지 않고 다음 공통 프롬프트를 전달합니다. 선행 조건이나 기록이 맞지 않아도 사용자가 원인을 직접 판정하지 않습니다. Codex가 안전하게 해결할 수 있는 문제는 기존 작업을 보존하면서 바로잡고 다시 검사합니다.

루트 AGENTS.md, docs/requirements/admin-platform.md와
docs/work-orders/05-session-protected-admin-route.md를 먼저 읽어 줘.
>
이 문서는 비전공자가 따라 하는 단계별 실습이야.
사용자에게 Git 상태, 문서 충돌이나 오류 원인을 직접 판정하게 하지 마.
>
먼저 다음 내용을 확인해 줘.
>
>1. 현재 단계와 목표
>2. Git 상태와 보존해야 할 기존 변경
>3. 이전 단계 완료 여부와 문서 간 현재 단계 기록
>4. 생성하거나 변경할 파일
>5. 실행할 자동 검사와 사용자가 확인할 WebUI 또는 대체 검사
>6. 위험, 충돌과 중단 조건
>
선행 조건이나 단계 기록이 맞지 않으면 보고만 하고 멈추지 말고 원인을 확인해 줘.
현재 코드와 확인된 검증 기록으로 이전 단계 완료를 확인할 수 있는데 README.md,
docs/requirements/admin-platform.md, docs/development-guides/local-development.md 또는
docs/development-guides/webui-gate.md의 단계 기록만 오래되었거나 서로 다르면,
요구사항과 기존 기록을 삭제하지 말고 실제 상태에 맞게 서로 일치시킨 뒤 다시 검사해 줘.
>
누락된 문서 생성, 잘못된 경로·파일명, 안전한 설정 수정처럼 현재 단계 범위에서 복구할 수 있는 문제는
기존 사용자 변경을 보존하면서 직접 복구하고 관련 검사를 다시 실행해 줘.
파일 삭제, 기존 변경 덮어쓰기, Git Reset, Secret 입력, sudo 권한이나 사용자의 실제 WebUI 확인처럼
사용자 결정이나 조작이 꼭 필요한 일은 임의로 진행하지 마.
그 경우에는 사용자가 해야 할 행동 한 가지만 쉬운 문장으로 설명하고, 복사할 명령이 있으면 코드 블록 하나로 제공한 뒤 결과를 기다려 줘.
>
선행 조건이 해결되면 별도의 재요청을 기다리지 말고 정식 작업 지시서의 허용 범위만 구현해 줘.
제외 범위와 다음 단계 기능은 추가하지 마.
실행하지 못한 검사는 통과로 기록하지 말고 필수 검사 실패를 완료로 판정하지 마.
>
자동 검사가 끝나면 사용자가 직접 확인해야 할 WebUI 또는 대체 검사만 번호가 있는 짧은 목록으로 안내해 줘.
검증 결과 기록, 스테이징과 커밋은 사용자가 실제 확인을 마친 뒤 별도 Prompt로 요청할 것이므로 지금 실행하지 마.
>
마지막에는 비전공자도 이해할 수 있게 다음 내용만 보고해 줘.
>
>7. 자동으로 해결한 문제
>8. 구현한 내용과 변경 파일
>9. 통과·실패·미수행 검사
>10. 사용자가 지금 확인할 항목
>11. 다음 절차 진행 가능 여부
Codex CLI에 05단계 공통 프롬프트를 전달하고 사전 확인을 요청한 화면

5. Codex의 자동 확인·복구 후 구현

위 프롬프트는 사전 확인에서 안전하게 해결할 수 있는 문제를 발견하면 Codex가 기존 작업을 보존하면서 직접 복구하고 다시 검사한 뒤 구현을 계속하도록 요청합니다. 사용자는 Git 상태, 문서 간 단계 충돌과 명령 오류를 직접 판정하지 않습니다.

Codex가 사용자 조작이 필요한 문제를 발견하면 한 번에 한 가지 행동만 안내합니다. 안내된 명령이나 WebUI 확인을 수행한 뒤 결과 전체를 같은 Codex CLI 대화에 붙여 넣습니다. Codex가 다음 절차 진행 가능이라고 보고하면 아래 구현 결과 확인으로 이동합니다. 진행 불가라고 보고하면 임의로 다음 단계로 넘어가지 않고, 마지막 보고 전체를 그대로 Codex에 다시 전달하여 해결을 계속 요청합니다.

backend/.envSESSION_SECRET이 없다고 표시되는 경우

다음 메시지는 세션 쿠키를 안전하게 서명하는 데 사용할 비밀값이 backend/.env에 아직 없다는 뜻입니다.

backend/.env에 SESSION_SECRET이 없습니다. 해당 파일에 충분히 긴 임의의 값을 SESSION_SECRET=... 형식으로 한 줄 추가한 뒤 알려 주세요.
Codex가 backend 환경 파일의 SESSION_SECRET 누락을 알리고 값 추가를 요청한 화면

SESSION_SECRET은 로그인 세션이 임의로 변조되지 않았는지 확인하는 데 사용하는 로컬 비밀값입니다. 메시지의 ...를 그대로 입력하거나 문서의 공개 실습용 암호를 재사용하지 않습니다. 실제 값은 Codex 대화, 명령 인자, 화면 캡처, 문서와 Git 추적 파일에 기록하지 않습니다.

프로젝트 루트에서 다음 명령을 실행합니다. 이 명령은 기존 backend/.env를 덮어쓰지 않으며, SESSION_SECRET이 없을 때만 64자리 16진수 임의 값을 생성해 파일 끝에 추가합니다. 생성한 값 자체는 터미널에 출력하지 않습니다.

VS Code의 Terminal 메뉴에서 SESSION_SECRET 준비 명령을 실행할 새 터미널을 여는 화면
cd /home/apple2ne1/projects/vibe-coding-platform
test -s backend/.env || { echo "backend/.env가 없거나 비어 있습니다. Codex에 이 결과를 전달하세요."; exit 1; }

if grep -q '^SESSION_SECRET=' backend/.env; then
  echo "SESSION_SECRET이 이미 있습니다. 중복해서 추가하지 않습니다."
else
  session_secret_value="$(openssl rand -hex 32)" || { echo "임의 값 생성에 실패했습니다. Codex에 이 결과를 전달하세요."; exit 1; }
  test -n "$session_secret_value" || { echo "임의 값이 비어 있습니다. Codex에 이 결과를 전달하세요."; exit 1; }
  printf 'SESSION_SECRET=%s\n' "$session_secret_value" >> backend/.env
  unset session_secret_value
  echo "SESSION_SECRET을 backend/.env에 추가했습니다."
fi
VS Code 원격 터미널에서 비밀값을 출력하지 않고 SESSION_SECRET을 생성해 backend 환경 파일에 추가한 화면

다음 명령으로 실제 값을 출력하지 않고 변수 저장 여부와 Git 제외 상태를 확인합니다.

grep -q '^SESSION_SECRET=.' backend/.env && echo "SESSION_SECRET 저장 확인 완료" || echo "SESSION_SECRET 확인 필요"
git check-ignore -v backend/.env
VS Code 원격 터미널에서 SESSION_SECRET 저장 여부와 backend 환경 파일의 Git 제외 상태를 확인한 화면

VS Code 직접 확인:

VS Code 편집기에서 공개 실습용 암호와 SESSION_SECRET을 포함한 backend 환경 파일의 변수 구성을 확인한 화면
VS Code 편집기에서 backend 환경 파일을 제외하고 예시 파일만 추적하는 gitignore 규칙을 확인한 화면

SESSION_SECRET 저장 확인 완료가 표시되고, 두 번째 명령에 .gitignore 규칙과 backend/.env 경로가 표시되어야 합니다. SESSION_SECRET 확인 필요가 표시되거나 git check-ignore 결과가 없으면 다음 단계로 넘어가지 말고 해당 출력만 Codex에 전달하여 해결을 요청합니다.

확인이 끝나면 실제 비밀값을 복사하지 않고 같은 Codex CLI 대화에 다음 문장만 전달합니다.

SESSION_SECRET 준비 완료
Codex CLI에 실제 비밀값을 노출하지 않고 SESSION_SECRET 준비 완료를 전달한 화면

구현 결과 WebUI 실행과 확인

05단계는 04단계의 MySQL 관리자 로그인을 유지하면서 서버 세션, /api/auth/me, 로그아웃과 보호된 /admin 경로를 확인하는 단계입니다. 자동 검사만 통과한 상태에서는 완료로 판정하지 않습니다. 실제 웹 브라우저에서 로그인 → 관리자 화면 이동 → 새로고침 뒤 세션 복원 → 로그아웃 → 비로그인 직접 접근 차단 흐름을 순서대로 확인해야 합니다.

WebUI 확인이 필수인데 화면이 열리지 않거나 기능이 동작하지 않으면 다른 검사의 통과 여부만으로 다음 단계로 이동하지 않습니다. 백엔드 터미널 오류와 웹 브라우저의 Console·Network 결과를 Codex에 전달하여 원인을 확인하고 복구합니다.

Codex가 자동 검사 후 사용자에게 로그인과 세션 및 WebUI 확인 항목을 안내한 화면

1. WebUI 확인 전 준비 상태 점검

프로젝트 루트에서 MySQL 서비스 상태와 SESSION_SECRET 저장 여부를 확인합니다. 다음 명령은 실제 SESSION_SECRET 값을 출력하지 않습니다.

cd /home/apple2ne1/projects/vibe-coding-platform
docker compose ps mysql
grep -q '^SESSION_SECRET=.' backend/.env && echo "SESSION_SECRET 저장 확인 완료" || echo "SESSION_SECRET 확인 필요"
git check-ignore -v backend/.env
VS Code 원격 터미널에서 MySQL 상태와 SESSION_SECRET 저장 및 backend 환경 파일의 Git 제외 상태를 확인한 화면

다음 세 조건을 모두 만족해야 합니다.

  • MySQL 서비스가 실행 중이고 healthy 상태임
  • SESSION_SECRET 저장 확인 완료가 표시됨
  • git check-ignore 결과에 .gitignore 규칙과 backend/.env 경로가 표시됨

MySQL 서비스가 중지되어 있으면 다음 명령으로 시작합니다.

docker compose up -d mysql
docker compose ps mysql

처음 시작하여 health: starting 또는 starting으로 표시되면 잠시 기다린 뒤 docker compose ps mysql을 다시 실행합니다. unhealthy, exited 또는 연결 오류가 표시되면 백엔드와 프런트엔드를 실행하지 않고 다음 로그에서 실제 암호를 제외한 오류 부분만 Codex에 전달합니다.

docker compose logs --tail=100 mysql

04단계의 users 테이블과 관리자 시드가 준비되지 않았거나 SESSION_SECRET 확인 필요가 표시되면 WebUI 확인으로 넘어가지 않습니다. MySQL 준비 문제는 04단계 MySQL 준비 절차를 먼저 확인하고, SESSION_SECRET 문제는 위 backend/.envSESSION_SECRET이 없다고 표시되는 경우 절차를 수행합니다.

2. 백엔드와 프런트엔드 실행

MySQL이 healthy이고 SESSION_SECRET 저장을 확인한 뒤 VS Code에서 터미널을 두 개 열어 백엔드와 프런트엔드를 각각 실행합니다.

VS Code의 Terminal 메뉴에서 백엔드와 프런트엔드를 실행할 분할 터미널을 여는 화면
# 터미널 1: 백엔드
cd /home/apple2ne1/projects/vibe-coding-platform/backend
npm run dev

# 터미널 2: 프런트엔드
cd /home/apple2ne1/projects/vibe-coding-platform/frontend
npm run dev
VS Code 분할 터미널에서 Express 백엔드와 Vite 프런트엔드 개발 서버가 정상 실행된 화면

백엔드 터미널에 데이터베이스 연결 오류, SESSION_SECRET 누락 또는 세션 초기화 오류가 없어야 합니다. 프런트엔드 터미널에는 Windows 관리 PC에서 열 수 있는 Vite 접속 주소가 표시되어야 합니다.

백엔드가 열리지 않거나 프런트엔드 주소가 표시되지 않으면 WebUI 확인으로 넘어가지 않습니다. 오류 출력 전체에서 실제 암호, 세션 ID와 쿠키 값을 제외한 부분만 같은 Codex CLI 대화에 전달하여 복구를 요청합니다.

3. 비로그인 직접 접근 차단 확인

로그인하기 전에 Windows 관리 PC의 웹 브라우저에서 http://서버-IP:5173/admin을 직접 엽니다. 이 문서의 검증 환경에서는 http://192.168.1.231:5173/admin을 사용합니다.

다음 결과를 확인합니다.

  1. 관리자 화면의 보호된 내용이 먼저 노출되지 않습니다.
  2. 로그인 화면으로 이동하거나 로그인 화면이 표시됩니다.
  3. 개발자 도구의 Network에서 GET /api/auth/me가 비로그인 상태를 나타내는 응답을 반환합니다.
  4. Console에 예상하지 못한 오류가 표시되지 않습니다.

관리자 화면이 잠깐이라도 먼저 보이거나 비로그인 상태에서 보호된 내용을 사용할 수 있으면 05단계를 완료하지 않습니다.

4. 관리자 로그인과 세션 생성 확인

웹 브라우저에서 http://서버-IP:5173을 열고 04단계에서 준비한 공개 실습용 관리자 이메일 admin@example.test와 암호 vibe-admin-04-demo로 로그인합니다.

Vibe Admin 로그인 화면에 공개 실습용 관리자 이메일과 암호를 입력한 화면

다음 순서로 확인합니다.

  1. 로그인 요청이 성공하고 /admin으로 이동합니다.
  1. 관리자 대시보드와 로그인 성공 상태가 표시됩니다.
관리자 로그인 성공 후 세션이 생성되고 Vibe Admin 대시보드가 표시된 화면
  1. F12를 눌러 개발자 도구를 열고, Network에서 로그인 요청이 성공 상태인 2xx로 응답하는지 확인합니다.
Edge 개발자 도구에서 로그인과 현재 사용자 확인 요청의 상태 및 Issues 문제 없음을 확인한 화면

GET /api/auth/me는 현재 인증 상태를 확인하는 요청이므로 200 응답과 최신 응답 내용을 기준으로 확인합니다. 이전 인증 상태가 캐시에서 재사용되지 않도록 실제 구현의 응답 헤더에 Cache-Control: no-store가 적용되었는지도 함께 확인합니다. 304 Not Modified가 표시되면 정상으로 단정하지 말고 응답 헤더와 캐시 설정을 Codex에 전달하여 검토를 요청합니다.

  1. 로그인 성공 응답에 실제 암호, password_hash, SESSION_SECRET, 세션 ID와 쿠키 값이 포함되지 않는지 확인합니다.
  1. 개발자 도구의 Application 또는 Storage 영역에서 vibe.sid 쿠키 이름이 존재하는지만 확인합니다.

개발자 도구의 추가 패널 메뉴에서 Application을 선택합니다. Application 메뉴가 안 보이면 Network 메뉴 오른쪽의 +를 클릭한 뒤 펼쳐진 메뉴에서 선택합니다.

Edge 개발자 도구의 추가 패널 메뉴에서 Application을 선택하는 화면

Application 패널이 열리면 왼쪽의 Storage 영역을 확인합니다.

Edge 개발자 도구의 Application 패널에서 Storage 항목을 확인한 화면

왼쪽 목록에서 Cookies를 선택하여 사이트별 쿠키 목록을 펼칩니다.

Edge 개발자 도구의 Application 패널에서 Cookies 항목을 선택한 화면

현재 접속 주소인 http://192.168.1.231:5173을 선택합니다.

Edge 개발자 도구의 Cookies 목록에서 현재 Vite 접속 주소를 선택한 화면

쿠키 표에서 vibe.sid 이름, 쿠키 값, HttpOnly 설정과 SameSite=Lax를 확인합니다. 다음 화면의 쿠키 값은 로컬 실습 중 생성된 일시적인 값입니다.

Edge 개발자 도구에서 로컬 실습용 vibe.sid 쿠키 값과 HttpOnly 및 SameSite 속성을 확인한 화면

이 화면의 vibe.sid 값은 현재 로컬 실습 세션에서만 사용하는 일시적인 식별자이며 세션이 바뀌면 같은 값이 표시되지 않아도 정상입니다. 운영 환경의 쿠키 값은 복사하거나 문서와 화면 캡처에 기록하지 않습니다. HttpOnlySameSite=Lax 적용 여부를 함께 확인합니다. HTTP 개발 환경에서는 Secure가 꺼져 있을 수 있으며, 이는 이번 단계의 의도적인 개발용 설정입니다.

참고로, Console에 나타난 현재 메시지는 개발 모드에서 정상적으로 볼 수 있는 항목이 포함되어 있습니다. 실제로 수정할 항목은 favicon.ico 누락이며, 401은 발생 시점에 따라 정상 또는 세션 오류로 구분합니다.

Edge 개발자 도구 Console에 React와 Vite 안내 및 favicon 404와 로그인 전 인증 401이 표시된 화면

5. 새로고침과 /api/auth/me 확인

로그인된 /admin 화면에서 웹 브라우저를 새로 고칩니다.

개발자 도구 오른쪽 위의 More toolsNetwork를 선택하여 요청 기록을 확인할 수 있는 상태로 준비합니다.

Edge 개발자 도구의 More tools 메뉴에서 Network를 선택하는 화면
  • F5 → 일반 새로고침
  • Ctrl + R → 일반 새로고침
  • 브라우저 왼쪽 위의 ↻ 새로고침 버튼 클릭

이번 테스트에서는 F5만 누르면 충분합니다.

Network가 열리고 요청 기록이 시작된 상태에서 F5를 눌러 새로 고칩니다.

관리자 화면에서 Network를 열고 새로고침을 준비한 화면
  1. 로그인 화면으로 돌아가지 않고 관리자 화면이 다시 표시되는지 확인합니다.
  2. 새로고침 중 인증 확인이 끝나기 전에 보호된 화면이 잘못 표시되거나 반복해서 깜빡이지 않는지 확인합니다.
  3. Network에서 GET /api/auth/me 요청이 성공 상태인 2xx로 응답하는지 확인합니다.
  4. /api/auth/me 응답에 현재 관리자 식별에 필요한 안전한 정보만 있고 실제 암호, password_hash, SESSION_SECRET, 세션 ID와 쿠키 값이 없는지 확인합니다.

새로고침 뒤 로그인 상태가 사라지면 04단계의 정상 동작이 아니라 05단계 세션 복원 실패입니다. 백엔드 터미널과 GET /api/auth/me의 상태 코드 및 민감정보를 제외한 응답을 Codex에 전달하여 해결합니다.

다음 화면처럼 로그인 페이지로 이동하면서 logout 요청이 200으로 표시되었다면 새로고침 실패가 아니라 로그아웃 요청이 정상 처리된 것입니다. 이 결과는 다음 절의 로그아웃 검사와 구분하여 확인합니다.

로그인 화면으로 이동하고 Network에 logout 요청의 200 응답이 표시된 화면

6. 로그아웃과 세션 제거 확인

관리자 화면에서 로그아웃을 실행하고 다음 결과를 확인합니다.

로그아웃하기 전에 개발자 도구의 Application 또는 Storage 영역에서 vibe.sid 쿠키가 존재하는지 확인합니다.

관리자 화면에서 로그아웃 전에 vibe.sid 쿠키가 존재하는지 확인하는 화면

Logout을 선택하여 로그인 화면으로 이동한 뒤 같은 쿠키 저장소를 다시 확인합니다. 다음 화면처럼 쿠키 목록이 비어 있으면 vibe.sid가 제거된 것입니다.

로그아웃 뒤 로그인 화면으로 이동하고 vibe.sid 쿠키가 제거된 화면

Network에서는 logout 요청의 Status200인지 확인합니다. loginme 같은 이전 요청이 함께 보일 수 있으므로 요청 이름이 logout인 행을 기준으로 판정합니다.

로그인 화면의 Network에서 logout 요청이 200으로 응답한 것을 확인하는 화면
  1. 로그아웃 요청이 성공 상태인 2xx로 응답합니다.
  2. 로그인 화면으로 이동합니다.
  3. vibe.sid 쿠키가 제거되거나 더 이상 유효한 세션으로 사용되지 않습니다.
  4. 웹 브라우저에서 /admin을 다시 직접 열어도 로그인 화면으로 이동합니다.
  5. GET /api/auth/me가 비로그인 상태를 나타내는 응답을 반환합니다.

화면만 로그인 페이지로 바뀌고 /admin을 다시 열었을 때 관리자 화면이 표시되면 서버 세션 또는 쿠키 제거가 완료되지 않은 것입니다. 이 경우 로그아웃을 완료로 기록하지 않습니다.

위 다섯 항목까지 확인했다면 이번 실습의 검증은 모두 끝났습니다. 별도의 권한·보안 자동 검사나 WebUI 완료 판정을 직접 수행하지 않고 다음 단계로 넘어갑니다.

7. 실습 프로세스 중지

WebUI 확인이 끝나면 프런트엔드 → 백엔드 → MySQL 순서로 중지합니다. VS Code의 터미널 목록에서 각 프로세스를 실행한 터미널을 하나씩 선택합니다.

1. 프런트엔드 중지

프런트엔드를 실행한 터미널을 선택하고 다음 중 한 가지 방법으로 Vite를 중지합니다.

q를 입력한 뒤 Enter

또는 해당 터미널에서 Ctrl+C를 누릅니다. Vite 실행 메시지 아래에 apple2ne1@...:~/projects/vibe-coding-platform$와 같은 셸 프롬프트가 다시 표시되면 프런트엔드가 중지된 것입니다.

2. 백엔드 중지

백엔드를 실행한 터미널을 선택하고 Ctrl+C를 누릅니다.

Ctrl+C

Backend listening on http://127.0.0.1:3000 같은 실행 메시지 아래에 셸 프롬프트가 다시 표시되면 백엔드의 tsx watch 프로세스가 중지된 것입니다.

다음 화면처럼 프런트엔드 터미널에는 q 입력 뒤 셸 프롬프트가, 백엔드 터미널에는 ^C 뒤 셸 프롬프트가 표시되면 두 프로세스가 모두 중지된 것입니다.

VS Code 원격 터미널에서 프런트엔드는 q로 종료하고 백엔드는 Ctrl+C로 종료한 화면

3. 프런트엔드와 백엔드 포트 확인

별도 터미널에서 3000번과 5173번 포트의 수신 프로세스가 남아 있지 않은지 확인합니다.

if ss -ltn | grep -Eq ':(3000|5173)\b'; then
  echo "확인 필요: 3000 또는 5173 포트의 프로세스가 아직 실행 중입니다"
else
  echo "백엔드와 프런트엔드가 중지되었습니다"
fi

백엔드와 프런트엔드가 중지되었습니다가 표시되면 다음 단계로 이동합니다.

VS Code 원격 터미널에서 3000번과 5173번 포트의 프로세스가 중지되었음을 확인한 화면

확인 필요가 표시되면 다른 프로세스를 임의로 강제 종료하지 않습니다. VS Code의 터미널 목록에서 이번 실습에 사용한 백엔드·프런트엔드 터미널을 찾아 앞에서 안내한 종료 키를 다시 사용한 뒤 포트를 재확인합니다.

4. MySQL 컨테이너 중지

백엔드와 프런트엔드가 중지된 뒤 MySQL은 데이터를 유지하면서 서비스만 중지합니다.

cd /home/apple2ne1/projects/vibe-coding-platform
docker compose stop mysql

Stopped 또는 컨테이너가 중지되었다는 메시지가 표시되면 다음 명령으로 상태를 확인합니다.

docker compose ps mysql

실행 중인 MySQL 컨테이너가 목록에 표시되지 않으면 중지가 완료된 것입니다. 상태를 다시 자세히 확인하려면 다음 명령을 사용할 수 있습니다.

VS Code 원격 터미널에서 MySQL 컨테이너를 중지하고 실행 목록이 비어 있는지 확인한 화면
docker compose ps -a mysql

STATUSExited로 표시되면 정상적으로 중지된 것입니다.

docker compose stop mysql은 MySQL 컨테이너를 중지하지만 이름 있는 볼륨의 데이터는 유지합니다. 볼륨을 삭제하는 docker compose down -v는 실행하지 않습니다.

8. Codex에 검증 결과 기록 요청

사용자가 위 절차의 WebUI 또는 대체 검사를 마친 뒤에는 여러 기록 파일을 직접 열어 맞추지 않습니다. 검증 결과가 성공이든 실패든 다음 프롬프트 하나를 Codex CLI에 전달하여 확인된 결과와 단계 상태를 정리합니다.

05단계 구현과 검증은 앞 절차에서 완료했어. 검증을 다시 실행하지 말고 현재 작업 기록과 실제 결과만 확인해 줘.
webui-gate.md에는 세션·보호 Route·로그아웃 결과를, local-development.md에는 로그인·세션 확인과 중지 방법을, README.md에는 보호 Route와 MemoryStore 제한을 기록해 줘. SESSION_SECRET 같은 새 변수는 실제로 추가된 경우에만 예시 파일에 변수 이름과 안전한 대체 값만 기록해 줘.
실행하지 않은 검사는 통과로 기록하지 말고 기존 기록을 삭제하거나 덮어쓰지 마.
현재 코드와 확인된 결과를 기준으로 README.md, docs/requirements/admin-platform.md,
docs/development-guides/local-development.md와 docs/development-guides/webui-gate.md의
현재 단계와 완료 상태가 서로 다른지도 확인해 줘.
기록만 오래되었거나 충돌하면 요구사항 자체를 바꾸지 말고 실제 상태에 맞게 일치시켜 줘.

실패나 미수행 결과가 있으면 완료로 만들지 말고 원인과 다음 해결 방법을 기록해 줘.
안전하게 해결할 수 있는 기록 누락이나 충돌은 직접 수정하고 다시 확인해 줘.
사용자 조작이 꼭 필요하면 사용자가 해야 할 행동 한 가지만 쉬운 문장으로 알려 줘.

마지막에는 비전공자도 이해할 수 있게 다음 내용만 보고해 줘.

1. 변경한 파일
2. 파일별로 기록한 실제 결과
3. 현재 단계 완료 기록이 서로 일치하는지
4. 남은 실패·미수행·확인 불가 항목
5. 다음 단계 진행 가능 여부

스테이징과 커밋은 실행하지 마.

코드 블록 전체를 Codex CLI에 붙여 넣고 Enter를 눌러 실행합니다. 다음 화면처럼 마지막 요청 사항까지 입력되었는지 확인하고 완료 보고가 나올 때까지 기다립니다.

VS Code 원격 터미널의 Codex CLI에 검증 결과 기록 프롬프트를 전달한 화면

완료 보고에서는 변경한 파일, 파일별 실제 기록, 현재 단계의 일치 여부, 남은 실패·미수행 항목과 다음 단계 진행 가능 여부를 확인합니다. 스테이징과 커밋은 실행하지 않았습니다라는 안내가 표시되어야 다음 절에서 사용자가 직접 변경 내용을 검토하고 커밋할 수 있습니다.

Codex CLI가 검증 기록 결과와 다음 단계 진행 가능 여부를 보고한 화면

9. 최종 변경 검토와 커밋

Codex의 완료 보고를 확인한 뒤 사용자가 이번 단계의 변경 파일을 스테이징합니다. Windows 관리 PC에서는 자동 검사 결과와 스테이징 파일 목록을 확인합니다.

cd /home/apple2ne1/projects/vibe-coding-platform
git status --short
git add .
git --no-pager diff --cached --name-only
git diff --cached --check

먼저 git status --short 결과에서 이번 단계에서 변경하거나 추가한 파일만 표시되는지 확인합니다. M은 수정된 파일, A는 새로 추가되어 추적되는 파일, ??는 아직 스테이징하지 않은 새 파일을 뜻합니다.

VS Code 원격 터미널에서 05단계 커밋 전 git status 결과를 확인한 화면

스테이징 파일 목록이 Codex의 완료 보고와 같고 git diff --cached --check가 오류 없이 종료되면 단계 결과를 커밋합니다. 목록이 다르거나 검사가 실패하면 커밋하지 않고 Codex에 출력 결과를 전달해 수정을 요청합니다.

git add .을 실행한 뒤 git diff --cached --name-only에 이번 단계의 변경 파일이 모두 표시되고, git diff --cached --check 다음에 오류 없이 셸 프롬프트가 다시 나타나는지 확인합니다.

VS Code 원격 터미널에서 스테이징 파일 목록과 git diff cached 검사를 확인한 화면
git commit -m "feat: complete 05 step"

커밋 식별자와 함께 변경된 파일 수가 표시되고 셸 프롬프트가 다시 나타나면 커밋이 완료된 것입니다.

VS Code 원격 터미널에서 05단계 변경 사항의 Git 커밋을 완료한 화면

문서·검증만 수행한 단계라면 Codex가 완료 보고에 제시한 docs:, test: 또는 chore: 메시지를 사용합니다. 필수 검사가 실패하면 커밋으로 완료 상태를 만들거나 다음 단계 작업 지시서를 실행하지 않습니다.

정식 작업 지시서 해설

이 절은 새로운 작업을 지시하거나 명령을 다시 실행하는 단계가 아닙니다. 정식 작업 지시서 실행, 구현 결과 검증, 검증 기록 갱신과 커밋까지 끝낸 뒤 05단계의 목적과 동작 원리를 되짚는 복습용 해설입니다.

앞에서 실제로 확인한 결과를 기준으로 인증 상태가 어떻게 유지되는지, 프런트엔드와 백엔드의 보호 경계가 어떻게 다른지, 무엇을 완료로 판정했는지를 설명합니다. 이후 단계에서 추가할 기능을 05단계에서 이미 완성한 것처럼 확대하여 해석하지 않습니다.

1. 04단계에서 이어받은 상태

04단계에서는 MySQL의 실제 관리자 계정으로 로그인할 수 있게 했지만, 로그인 성공 여부를 프런트엔드 메모리에만 두었기 때문에 새로고침하면 관리자 상태가 사라졌습니다. 05단계는 MySQL 사용자 검증을 그대로 유지하면서 서버 세션을 추가하여 이 제한을 해결합니다.

로그인 성공 뒤 서버가 세션을 만들고, 프런트엔드는 그 세션을 기준으로 보호된 /admin 화면을 표시하게 했습니다. 백엔드에는 보호 API에 공통으로 적용할 requireAdmin 권한 검사 경계를 마련했습니다. 05단계에서 명시적으로 추가한 인증 엔드포인트는 GET /api/auth/mePOST /api/auth/logout이며, 실제 관리자 기능 API는 이후 단계에서 이 경계를 적용하여 추가합니다.

05단계에서 채워진 주요 폴더와 파일

vibe-coding-platform/
├── docs/
│   └── work-orders/
│       └── 05-session-protected-admin-route.md
├── backend/
│   └── src/
│       ├── <Session 설정과 인증 경로의 실제 소스 파일>
│       └── <requireAdmin Middleware의 실제 소스 파일>
└── frontend/
    └── src/
        └── <Login 상태 확인과 보호 경로의 실제 소스 파일>

05단계는 기존 backend/src/frontend/src/ 안에 세션·권한 경계를 추가합니다. 정식 작업 지시서가 소스 파일명을 고정하지 않았으므로 구현 완료 보고에서 확인한 실제 이름을 사용하며, 같은 역할의 새 최상위 폴더를 만들지 않습니다.

2. 인증과 권한 확인을 나누는 이유

인증은 요청을 보낸 사용자가 누구인지 확인하는 과정이고, 권한 확인은 그 사용자가 관리자 기능을 사용할 수 있는지 판단하는 과정입니다. 로그인에 성공했다는 사실만으로 모든 관리자 API를 허용하면 안 됩니다.

requireAdmin 미들웨어는 보호된 요청마다 세션의 userId로 MySQL 사용자를 다시 조회하고, 사용자가 존재하는지, is_active가 활성 상태인지, roleadmin인지 확인합니다. 역할과 활성 상태를 세션에 복사해 두고 계속 신뢰하지 않으므로, 계정이 비활성화되거나 역할이 바뀌면 기존 세션이 남아 있어도 관리자 접근을 거부할 수 있습니다.

3. 세션과 쿠키의 동작 원리

로그인 성공 시 기존 세션 ID를 그대로 사용하지 않고 regenerate()로 새 식별자를 발급합니다. 이는 로그인 전 세션 ID가 로그인 뒤에도 유지되는 세션 고정 위험을 줄이기 위한 조치입니다. 세션에는 사용자 전체 정보나 역할을 복사하지 않고 userId만 저장합니다.

실제 세션 데이터는 서버에 있고, 웹 브라우저에는 해당 세션을 찾기 위한 vibe.sid 쿠키만 전달됩니다. HttpOnly는 프런트엔드 JavaScript가 쿠키 값을 직접 읽지 못하게 하고, SameSite=Lax는 다른 사이트에서 시작된 일부 요청에 쿠키가 포함되는 위험을 줄입니다. 그러나 두 설정만으로 모든 XSS와 CSRF 위험이 해결되는 것은 아닙니다.

SESSION_SECRET은 쿠키 내용을 암호화하는 값이 아니라 서명된 세션 쿠키가 변조되었는지 검증하는 비밀값입니다. HTTP 개발 환경에서는 secure=false를 사용하지만, HTTPS 운영 환경에서는 Secure 쿠키와 실제 프록시 구성을 함께 검증해야 합니다. SESSION_SECRET과 운영 환경의 세션 ID·쿠키 값은 소스, 로그, 문서와 화면 캡처에 기록하지 않습니다. 이 문서에서는 세션 동작을 확인하기 위해 로컬 연습 중 생성된 일회성 vibe.sid 값만 예외로 표시합니다.

4. 새로고침과 보호 경로가 동작하는 과정

프런트엔드가 시작되면 Auth ContextGET /api/auth/me를 호출하여 현재 세션을 확인합니다. 확인이 끝나기 전에는 보호된 /admin 화면을 먼저 보여 주지 않습니다. 유효한 관리자 세션이면 대시보드를 복원하고, 세션이 없거나 권한 검사가 실패하면 로그인 화면으로 이동합니다.

프런트엔드의 보호 경로는 잘못된 화면 노출을 막는 사용자 경험 장치입니다. 실제 보안 경계는 백엔드의 requireAdmin 미들웨어이므로, 사용자가 URL을 직접 입력하거나 프런트엔드 검사를 우회해도 보호 API가 요청을 거부해야 합니다.

로그아웃에서는 서버 세션을 삭제한 뒤 세션 쿠키도 제거합니다. 화면만 로그인 페이지로 바꾸고 서버 세션을 남겨 두면 같은 쿠키로 다시 접근할 수 있으므로 두 작업이 모두 성공해야 합니다. 로그아웃 뒤 GET /api/auth/me가 비로그인 응답을 반환하고 /admin 직접 접근이 차단되어야 로그아웃이 완료된 것입니다.

5. 사용자 확인과 Codex 자동 검사의 범위

  • 사용자는 앞의 WebUI 절차에서 비로그인 상태의 /admin 직접 접근 차단, 관리자 로그인, 새로고침 뒤 관리자 화면 유지, GET /api/auth/me, 로그아웃과 쿠키 무효화를 순서대로 확인합니다.
  • 비전공자가 데이터베이스의 사용자 상태를 직접 바꾸거나 별도의 보안 검사를 실행할 필요는 없습니다.
  • 일반 사용자와 비활성 사용자의 차단, 로그인 뒤 세션 ID 재발급 및 기존 04단계 로그인 흐름의 회귀 여부는 Codex의 완료 보고에 실제 실행 명령과 결과가 있는 경우에만 통과로 판단합니다. 실패하거나 실행하지 않은 항목은 해설만으로 통과한 것으로 간주하지 않습니다.
  • SESSION_SECRET과 운영 환경의 세션 ID·쿠키 값은 공개 값으로 취급하지 않습니다. 이 문서의 로컬 연습용 일회성 vibe.sid 값은 동작 확인 예외이며 운영 인증정보로 사용하지 않습니다.

앞의 ### 6. 로그아웃과 세션 제거 확인까지 마치면 사용자가 수행하는 WebUI 검증은 끝납니다. 일반 사용자·비활성 사용자 차단과 세션 ID 재발급처럼 별도 테스트 데이터나 기술 판단이 필요한 항목은 사용자가 데이터베이스를 직접 변경하지 않고 Codex의 자동 검사 결과로 확인합니다.

6. 개발용 MemoryStore의 한계

이 단계에서는 세션 동작을 먼저 학습하기 위해 express-session의 기본 MemoryStore를 사용합니다. 이 저장소는 백엔드 프로세스를 다시 시작하면 세션이 사라지고, 여러 백엔드 인스턴스가 같은 세션을 공유할 수 없으며, 운영 환경의 지속성과 확장성 요구를 충족하지 못합니다.

따라서 MemoryStore 사용은 이번 단계의 의도적인 개발용 제한입니다. 06단계에서는 인증 구조를 유지한 채 관리자 화면 구성을 완성하고, 08단계에서 Redis 세션 저장소를 통합한 뒤 13단계에서 연결, 만료 시간과 장애 복구를 강화합니다.

7. 검증 기록을 구현과 분리한 이유

정식 작업 지시서는 세션과 보호 경로의 구현 범위 및 완료 조건만 유지합니다. 사용자가 WebUI 확인을 마친 뒤 별도의 8. Codex에 검증 결과 기록 요청으로 실제 확인 결과를 관련 문서에 반영합니다.

webui-gate.md에는 로그인·새로고침·보호 경로·로그아웃의 실제 결과를, local-development.md에는 SESSION_SECRET 준비와 세션 확인·중지 방법을 기록합니다. README.md에는 05단계 완료 상태와 개발용 MemoryStore 제한을 요약합니다. 실패하거나 수행하지 않은 검사는 통과로 기록하지 않습니다.

8. README.md에 남는 완료 상태의 의미

다음 예시는 필수 자동 검사와 WebUI 확인이 모두 통과하고 Codex의 검증 기록 갱신까지 끝났을 때 루트 README.md에 남는 완료 상태입니다.

## 현재 단계

05단계: 세션과 보호된 관리자 경로 만들기

## 완료된 기능

- 로그인 성공 시 재발급되는 서버 세션
- `HttpOnly` 세션 쿠키와 `/api/auth/me`
- 백엔드 `requireAdmin` 권한 검사
- 새로고침 후 유지되는 보호된 `/admin` 경로
- 로그아웃 시 세션과 쿠키 제거

## 보안 기준

- 세션에는 `userId`만 저장합니다.
- `SESSION_SECRET`은 `.env`에서 읽고 저장소에 기록하지 않습니다.

## 현재 제한

- 현재 세션 저장소는 개발용 `MemoryStore`입니다.

검사에 실패하거나 실행하지 않은 항목이 있으면 이 예시를 완료 상태로 기록하지 않으며, 06단계를 진행할 수 있다고 판단하지 않습니다.

9. 다음 단계

다음 글에서는 인증 구조를 그대로 유지하면서 최소 관리자 대시보드 화면 구성을 완성합니다.

참고 자료 및 출처


Previous article
Next article