1
핵심 요약
3단계의 백엔드 내부 임시 관리자 값을 삭제하고 MySQL에 저장한 사용자를 조회합니다.
React 로그인 폼
↓
Express 로그인 API
↓
MySQL에서 Email 조회
↓
암호 해시 비교
↓
role과 is_active 확인
↓
관리자 사용자 반환
이번 단계에서는 아직 세션을 만들지 않습니다. 로그인 성공 직후 대시보드로 이동하지만 새로고침하면 로그인 상태가 사라집니다.

이번 단계의 Vibe Coding 작업 지시서
학습용 간단한 지시 설명서
03단계 로그인 API의 임시 관리자 값을 MySQL 사용자 조회와 Argon2id 암호 검증으로 교체해 줘.
mysql2 Promise Pool과 Prepared Statement를 사용하고 관리자 시드 값은 환경 변수로 받아 줘.
일반 사용자와 비활성 사용자의 관리자 로그인을 차단하고 민감정보를 응답과 로그에 넣지 마.
세션, Redis와 Nginx는 추가하지 마.
변경 전·후 공통 WebUI Gate와 성공·실패 로그인 검증 결과를 보고해 줘.
정식 작업 지시서
다음 코드 블록 전체가 04단계 구현의 유일한 정식 작업 지시서입니다. 내용을 줄이거나 별도의 상세 Prompt로 복제하지 않고 docs/work-orders/04-mysql-admin-account.md에 저장합니다.
# 04단계 정식 작업 지시서
## 단계별 상세 지시
루트 AGENTS.md, 요구사항과 docs/work-orders/04-mysql-admin-account.md를 먼저 읽어 줘.
03단계 완료 확인표와 공통 WebUI Gate가 통과했는지 확인하고, 실패하면 구현을 중단해 줘.
허용 범위는 backend/, database/, 개발용 MySQL 서비스에 필요한 compose.yaml과 안전한 환경 변수 예시다.
프런트엔드 API 계약과 화면 흐름은 변경하지 않는다.
변경 전에 MySQL 연결 방식, 생성·변경 파일, Secret 전달 방법과 검증 명령을 보고한다.
스키마와 Seed를 적용하기 전에 현재 MySQL 서비스, 이름 있는 볼륨, 대상 데이터베이스와 기존 users 데이터를 읽기 전용으로 확인한다.
프로젝트에 실제로 존재하거나 현재 환경에서 검증된 Backup·Restore 절차를 찾아 사용하고, Backup 파일명 또는 저장 위치, 생성 시각, 대상 데이터베이스와 무결성 확인값으로 Backup을 식별한다.
원본 데이터베이스와 이름 있는 볼륨을 건드리지 않는 격리된 대상에 Restore하여 스키마와 핵심 데이터 건수를 확인한다.
검증된 Backup·Restore 절차가 없거나 격리 Restore가 실패하면 스키마와 Seed를 적용하지 말고 필요한 절차와 중단 이유를 보고한다. 확인되지 않은 Backup·Restore 명령을 추정해서 만들지 않는다.
적용 직전과 적용 직후에 대상 데이터베이스, users 테이블과 기존 행 수를 비교하고, 실패 시 복구할 데이터베이스·볼륨·설정 범위와 데이터 손실 가능성을 보고한다. 실제 Restore, 볼륨 교체 또는 데이터 삭제는 사용자의 명시적 승인을 받은 뒤에만 수행한다.
04단계의 compose.yaml에는 개발용 MySQL 서비스만 추가한다.
프런트엔드와 백엔드 서비스의 컨테이너 구성과 전체 Compose 통합은 08단계 범위로 남긴다.
MySQL은 127.0.0.1:3306에만 게시하고 이름 있는 볼륨에 데이터를 저장한다.
MySQL 초기화 값은 Git에서 제외한 .env.mysql에서 읽고 실제 값을 Compose 파일에 직접 기록하지 않는다.
users 테이블에 id, email, password_hash, role, is_active를 정의한다.
mysql2 Promise Pool과 Prepared Statement로 Email을 조회한다.
암호는 Argon2id 해시로 저장하고 백엔드에서 비교한다.
관리자 시드 이메일과 암호는 환경 변수로 받고 실제 값을 소스, 문서와 명령 인자에 기록하지 않는다.
backend/package.json의 기존 패키지와 스크립트 구성을 보존한다.
dev, start와 seed:admin은 backend/에서 실행할 때 Node의 --env-file=.env로 backend/.env를 읽도록 구성한다.
세 스크립트는 서로 다른 환경 파일이나 환경 변수 로딩 방식을 사용하지 않는다.
환경 파일이 없거나 필수 환경변수가 비어 있으면 실제 값을 출력하지 않고 안전한 오류로 중단한다.
일반 사용자, 비활성 사용자와 존재하지 않는 사용자의 관리자 로그인을 허용하지 않는다.
로그인 실패 응답은 이메일 존재 여부를 구분하지 않는다.
password_hash와 데이터베이스 상세 오류를 응답, 로그와 화면에 노출하지 않는다.
MySQL 포트를 외부 네트워크에 공개하지 않는다.
Session, Redis와 Nginx를 추가하지 않는다.
새로고침 후 로그인 상태가 사라지는 현재 단계의 제한을 오류로 수정하지 않는다.
변경 전·후 공통 WebUI Gate를 실행한다.
백엔드 typecheck, 프런트엔드 build, MySQL 연결, 관리자 성공, 잘못된 암호, 없는 사용자, 비활성 사용자와 일반 사용자를 검증한다.
dev, start와 seed:admin 스크립트에 --env-file=.env가 적용되었는지 확인한다.
시드와 백엔드 개발 서버가 같은 backend/.env를 읽는 상태에서 실제 MySQL 연결과 로그인을 다시 검증한다.
검사 결과는 완료 보고에 통과, 실패와 미수행으로 구분한다. 검증 기록 문서 갱신은 사용자가 WebUI 확인을 마친 뒤 별도 Prompt로 수행하므로 이 작업에서는 실행하지 않는다.
연결, 시드 또는 Gate가 실패하면 05단계 세션을 시작하지 말고 원인과 복구 방법을 보고한다.
## 완료 보고 형식
- 생성하거나 변경한 파일
- 실행한 명령과 실제 결과
- 통과, 실패와 미수행 검증
- 요구사항별 구현 내용과 변경 이유
- 남은 제한, 위험과 다음 단계 진행 가능 여부
정식 작업 지시서 저장과 실행 순서
다음 순서는 Windows 관리 PC의 VS Code에서 Remote SSH로 개발 서버에 연결한 상태를 기준으로 합니다. 앞 단계가 실패했거나 기존 변경과 충돌하면 다음 번호로 넘어가지 않습니다.
1. 프로젝트와 이전 단계 상태 확인
cd /home/apple2ne1/projects/vibe-coding-platform
pwd
git status --short

AI 코딩 에이전트에게 변경을 맡기기 전에 이전 단계의 완료 기록과 현재 사용자 변경을 구분합니다. 예상하지 못한 변경이 있으면 보존 방법을 먼저 결정합니다.
2. 정식 작업 지시서 저장
VS Code에서 docs/work-orders/04-mysql-admin-account.md를 만들고, 위 정식 작업 지시서 코드 블록의 내용을 빠짐없이 저장합니다. 간단한 지시서와 정식 작업 지시서를 서로 다른 실행 Prompt로 보내지 않습니다.
VS Code의 원격 터미널에서 프로젝트 루트로 이동하고 현재 상태를 확인합니다.
방법 1: VS Code 작업
Explorer에서 work-orders를 마우스 오른쪽 버튼으로 클릭하고 New File을 선택합니다.

파일 이름으로 04-mysql-admin-account.md를 입력하여 새 작업 지시서 파일을 만듭니다.

방법 2: 터미널 작업
mkdir -p docs/work-orders
touch docs/work-orders/04-mysql-admin-account.md
- 파일 존재여부 확인
- 저장 후 파일의 변경여부 확인
VS Code의 Explorer에서 docs/work-orders/04-mysql-admin-account.md를 선택해 열고, 위 정식 작업 지시서 코드 블록 전체를 복사해 붙여넣은 뒤 Ctrl+S로 저장합니다.

저장한 다음 VS Code 원격 터미널에서 파일 내용과 변경 사항을 확인합니다.
test -s docs/work-orders/04-mysql-admin-account.md
git diff -- docs/work-orders/04-mysql-admin-account.md
- 파일 존재여부 확인
- 저장 후 파일의 변경여부 확인

저장한 파일에 목표, 허용 범위, 제외 범위, 완료 조건, 검증과 중단 조건이 모두 포함되어 있는지 직접 확인합니다.
3. 작업 지시서 Git 기준점 기록
위 정식 작업 지시서 코드 블록 전체를 지정한 파일에 저장한 뒤, 해당 파일만 먼저 커밋하여 구현 기준을 고정합니다.
git status --short
test -s docs/work-orders/04-mysql-admin-account.md
git add -- docs/work-orders/04-mysql-admin-account.md
git diff --cached --name-only
git diff --cached --check
git commit -m "docs: add 04 step work order"

스테이징 파일 목록에 docs/work-orders/04-mysql-admin-account.md 하나만 표시되고 git diff --cached --check가 오류 없이 종료되면 기준점 커밋을 진행합니다. 다른 파일이 표시되거나 검사가 실패하면 커밋하지 않고 Codex에 표시된 파일과 오류를 전달해 수정을 요청합니다.
4. Codex CLI 실행과 공통 Prompt 전달
프로젝트 루트에서 Codex CLI를 실행합니다.
codex -C /home/apple2ne1/projects/vibe-coding-platform
Codex CLI에 단계별 구현 조건을 다시 길게 복사하지 않고 다음 공통 Prompt를 전달합니다. 선행 조건이나 기록이 맞지 않아도 사용자가 원인을 직접 판정하지 않습니다. Codex가 안전하게 해결할 수 있는 문제는 기존 작업을 보존하면서 바로잡고 다시 검사합니다.
루트 AGENTS.md, docs/requirements/admin-platform.md와
docs/work-orders/04-mysql-admin-account.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로 요청할 것이므로 지금 실행하지 마.
마지막에는 비전공자도 이해할 수 있게 다음 내용만 보고해 줘.
1. 자동으로 해결한 문제
2. 구현한 내용과 변경 파일
3. 통과·실패·미수행 검사
4. 사용자가 지금 확인할 항목
5. 다음 절차 진행 가능 여부
5. Codex의 자동 확인·복구 후 구현
위 Prompt는 사전 확인에서 안전하게 해결할 수 있는 문제를 발견하면 Codex가 기존 작업을 보존하면서 직접 복구하고 다시 검사한 뒤 구현을 계속하도록 요청합니다. 사용자는 Git 상태, 문서 간 단계 충돌과 명령 오류를 직접 판정하지 않습니다.
Codex가 사용자 조작이 필요한 문제를 발견하면 한 번에 한 가지 행동만 안내합니다. 안내된 명령이나 WebUI 확인을 수행한 뒤 결과 전체를 같은 Codex CLI 대화에 붙여 넣습니다. Codex가 다음 절차 진행 가능이라고 보고하면 아래 구현 결과 확인으로 이동합니다. 진행 불가라고 보고하면 임의로 다음 단계로 넘어가지 않고, 마지막 보고 전체를 그대로 Codex에 다시 전달하여 해결을 계속 요청합니다.
구현 결과 WebUI 실행과 확인
04단계는 웹 브라우저를 열기 전에 개발용 MySQL, users 테이블과 학습용 관리자 Seed가 준비되어야 합니다. 데이터베이스 준비를 생략하면 백엔드와 프런트엔드가 정상적으로 실행되어도 로그인 API는 사용자를 조회할 수 없습니다.
이 절에서는 04단계에서 구현한 실제 파일과 명령을 기준으로 개발용 MySQL 준비 → 스키마와 Seed 적용 → 데이터베이스와 API 검사 → WebUI 확인 순서로 진행합니다. 프런트엔드·백엔드 컨테이너화와 전체 Compose 프로젝트 통합은 08단계의 범위이므로 여기에서 미리 추가하지 않습니다.
1. 개발용 MySQL과 관리자 시드 준비
04단계의 실제 구현에 따라 MySQL 실행 방식, 환경 변수 이름, 스키마 파일과 시드 명령이 달라질 수 있습니다. 다음 프롬프트는 파일명을 추정하지 않고 현재 프로젝트를 먼저 확인한 뒤, 구현에 포함된 방식으로 데이터베이스를 준비하도록 요청합니다.
루트 AGENTS.md, docs/work-orders/04-mysql-admin-account.md와 현재 프로젝트 파일을 먼저 읽어 줘.
04단계에서 실제로 구현된 MySQL 실행 방식, Compose 서비스 이름 또는 컨테이너 실행 방식,
환경 변수 예시 파일, 실제 로컬 환경 파일 위치, 스키마 파일과 관리자 시드 스크립트를 찾아 줘.
파일명, 환경변수 이름이나 명령을 추정해서 새로 만들지 말고 실제 구현을 기준으로 사용해 줘.
>
먼저 다음 내용을 짧게 보고해 줘.
1. MySQL을 실행하는 실제 파일과 명령
2. MySQL 수신 주소와 포트 공개 범위
3. 데이터가 유지되는 볼륨 또는 저장 방식
4. users 테이블을 만드는 실제 스키마 파일과 적용 방식
5. 관리자 시드를 실행하는 실제 스크립트와 입력받는 환경 변수 이름
6. 데이터베이스와 API를 확인할 명령
스키마와 Seed를 적용하기 전에 다음 안전 Gate를 처리해 줘.
- 현재 MySQL 서비스, 이름 있는 볼륨, 대상 데이터베이스, users 테이블과 기존 데이터 상태를 읽기 전용으로 확인한다.
- 프로젝트에 실제로 존재하거나 현재 환경에서 검증된 Backup·Restore 절차를 찾는다.
- 검증된 절차가 있으면 Backup의 파일명 또는 저장 위치, 생성 시각, 대상 데이터베이스와 무결성 확인값을 기록하고, 원본 데이터베이스와 볼륨을 건드리지 않는 격리된 대상에 Restore하여 스키마와 핵심 데이터 건수를 확인한다.
- 검증된 절차가 없거나 격리 Restore가 실패하면 스키마와 Seed를 적용하지 말고 중단 이유와 먼저 확정해야 할 절차를 보고한다. 확인되지 않은 명령을 추정해서 만들지 않는다.
- 적용 전후의 대상 데이터베이스, users 테이블과 기존 행 수를 비교한다.
- 실패 시 복구할 데이터베이스·볼륨·설정 범위와 데이터 손실 가능성을 보고한다. 실제 Restore, 볼륨 교체와 데이터 삭제는 사용자의 명시적 승인 없이 실행하지 않는다.
실제 값이 들어가는 환경 파일이 없으면 비밀정보를 요청하거나 출력하지 마.
Git에 포함되는 예시 파일에서 필요한 변수 이름만 확인하고,
사용자가 로컬 환경 파일에 값을 입력해야 한다면 파일 경로와 입력할 변수 이름만 한 번에 한 단계로 안내한 뒤 기다려 줘.
환경 파일이 Git에서 제외되는지도 확인해 줘.
필요한 로컬 값이 준비되어 있으면 현재 구현 방식으로 개발용 MySQL만 실행해 줘.
프런트엔드·백엔드 컨테이너화, 운영 Compose 구성, Redis, Nginx와 다음 단계 기능은 추가하지 마.
MySQL이 준비되면 실제 구현 방식으로 스키마와 관리자 Seed를 적용해 줘.
Seed는 같은 명령을 다시 실행해도 관리자 행이 중복 생성되지 않는지 확인해 줘.
암호는 Argon2id Hash로 저장하고 실제 암호, 전체 password_hash와 인증정보를
명령 인자, 출력, 로그, 문서와 Git 추적 파일에 표시하지 마.
>
검사에서는 다음 항목만 안전하게 확인해 줘.
- MySQL 컨테이너 또는 프로세스 상태
- MySQL이 외부 네트워크에 공개되지 않았는지
- users 테이블과 필수 열 존재 여부
- 관리자 시드 행 수, role과 is_active
- 백엔드의 MySQL 연결
- 관리자 성공, 잘못된 암호, 없는 사용자, 비활성 사용자와 일반 사용자 로그인 판정
- 응답과 로그에 암호, password_hash와 데이터베이스 상세 오류가 없는지
실패한 항목을 자동으로 통과 처리하지 말고 원인과 안전한 복구 방법을 보고해 줘.
검증 기록 문서 갱신, 스테이징과 커밋은 아직 실행하지 마.
마지막에는 비전공자도 이해할 수 있게 다음 내용만 보고해 줘.
1. 실제로 사용한 MySQL 실행 방식과 명령에서 비밀정보를 가린 형태
2. 스키마와 시드를 적용한 실제 파일과 명령에서 비밀정보를 가린 형태
3. 생성하거나 확인한 데이터 구조와 시드 행 수
4. 통과·실패·미수행 검사
5. 사용자가 WebUI 확인으로 이동해도 되는지
6. 실습이 끝난 뒤 데이터는 유지하면서 MySQL만 중지하는 실제 명령
MySQL 실행 구현이 없다고 보고된 경우
현재 확인된 결과에서는 백엔드 typecheck와 단위 테스트 8건이 통과했습니다. 관리자 성공, 잘못된 암호, 없는 사용자, 비활성 사용자, 일반 사용자 거부와 민감정보 비노출 로직은 코드 수준에서 확인되었습니다.
그러나 compose.yaml에 서비스가 없고 실행 중인 MySQL 컨테이너나 프로세스도 없어 실제 백엔드–MySQL 연결은 실패했습니다. database/schema.sql과 backend/src/seed-admin.ts는 존재하지만 적용할 MySQL이 없으므로 실제 테이블·관리자 행·Seed 중복 방지·데이터베이스 로그인과 백엔드 로그 검사는 미수행 상태입니다. 이 결과만으로 WebUI 확인이나 05단계로 진행하지 않습니다.
다음 복구 Prompt는 04단계에 필요한 개발용 MySQL 서비스만 추가합니다. 프런트엔드와 백엔드의 컨테이너화와 전체 Compose 통합은 08단계 범위로 유지합니다.
루트 AGENTS.md, docs/work-orders/04-mysql-admin-account.md와 현재 프로젝트 파일을 다시 읽어 줘.
>
현재 확인된 상태는 다음과 같아.
>- compose.yaml에 서비스가 없음
>- 실행 중인 MySQL 컨테이너와 프로세스가 없음
>- database/schema.sql은 존재함
>- backend/src/seed-admin.ts와 npm run seed:admin은 존재함
>- Backend typecheck와 단위 테스트 8건은 통과함
>- 실제 Backend-MySQL 연결은 실패함
>- 실제 Schema, Seed, 테이블, 관리자 행과 Database Login 검사는 미수행임
>
기존 사용자 변경을 보존하면서 04단계에 필요한 개발용 MySQL 실행 구성만 구현해 줘.
compose.yaml의 기존 프로젝트 설정을 유지하고 MySQL 8.4 개발 서비스 하나만 추가해 줘.
서비스 이름은 프로젝트의 기존 명명 규칙을 먼저 확인해 정하고 마지막 보고에 정확히 알려 줘.
>
다음 조건을 모두 만족해야 해.
>- MySQL 포트는 127.0.0.1:3306:3306으로만 게시
>- 데이터는 이름 있는 볼륨에 저장
>- database/schema.sql은 MySQL 초기화 폴더에 읽기 전용으로 연결
>- MySQL 초기화 환경변수는 Git에서 제외한 .env.mysql에서 읽음
>- MYSQL_DATABASE, MYSQL_USER와 MYSQL_PASSWORD는 backend/.env의 값과 일치
>- MYSQL_ROOT_PASSWORD는 .env.mysql에만 저장하고 문서와 Git 추적 파일에 실제 값을 기록하지 않음
>- healthcheck를 추가하고 MySQL이 준비된 뒤에만 Schema와 Seed 검사를 진행
>- compose.yaml, .env.mysql.example과 .gitignore에는 실제 암호를 기록하지 않음
>
backend/.env와 .env.mysql에 필요한 로컬 값이 아직 없으면 임의의 값을 만들거나 출력하지 마.
필요한 파일 경로와 변수 이름만 안내하고 사용자 입력을 기다려 줘.
>
환경 파일이 준비되어 있으면 다음 순서로 실제 검사를 진행해 줘.
>1. docker compose config
>2. 개발용 MySQL 서비스 실행
>3. docker compose ps와 health 상태 확인
>4. 127.0.0.1:3306에만 수신하는지 확인
>5. database/schema.sql 적용과 users Table의 필수 Column 확인
>6. cd backend && npm run seed:admin 실행
>7. 같은 Seed를 다시 실행해 Admin 행이 중복되지 않는지 확인
>8. Admin 행 수, role과 is_active만 안전하게 확인
>9. 실제 Backend-MySQL 연결과 Database Login 성공·실패 검사
>10. Backend Log에 암호, password_hash와 Database 상세 오류가 없는지 확인
>
실제 암호, 전체 password_hash, Authorization 값과 환경 파일 내용은 출력하지 마.
실패한 검사는 통과로 기록하지 말고 원인과 안전한 복구 방법을 보고해 줘.
프런트엔드·백엔드 Container, Redis, Nginx, 운영 Compose 구성과 다음 단계 기능은 추가하지 마.
검증 기록 문서 갱신, 스테이징과 Commit은 아직 실행하지 마.
>
마지막에는 다음 내용만 보고해 줘.
>11. 변경한 파일
>12. 추가한 MySQL 서비스 이름, 포트 범위와 볼륨 이름
>13. 사용자가 준비해야 하는 로컬 환경 파일과 변수 이름
>14. 실행한 명령에서 비밀정보를 가린 형태
>15. Schema·Seed·연결 검사의 통과·실패·미수행 결과
>16. 다음에 사용자가 수행할 한 가지 작업
>17. WebUI 확인으로 이동 가능한지
>18. 데이터를 유지하면서 MySQL 서비스만 중지하는 명령
Codex가 환경 파일 준비를 요청하면 바로 아래 절차로 .env.mysql과 backend/.env를 준비합니다. 구현 오류를 보고하면 출력 전체를 같은 대화에 전달하여 복구합니다. MySQL 상태, 스키마, Seed와 실제 연결 검사가 끝나기 전에는 WebUI 확인으로 넘어가지 않습니다.
Codex가 환경 파일 준비를 요청한 경우
Codex가 MySQL 실행 구성을 추가한 뒤 환경 파일 준비를 요청하면 .env.mysql과 backend/.env를 차례로 준비합니다. 두 파일의 데이터베이스명, 애플리케이션 사용자와 애플리케이션 사용자 암호는 서로 같아야 합니다.
MySQL 컨테이너용 .env.mysql 준비
방법 1: VS Code 작업
VS Code의 Explorer에서 프로젝트 루트를 선택한 뒤 New File 아이콘을 선택합니다.

파일 이름으로 .env.mysql을 입력하고 Enter를 누릅니다.

방법 2: 터미널 작업
Codex가 생성한 .env.mysql.example을 Git에서 제외되는 로컬 파일 .env.mysql로 복사합니다. 기존 파일이 있으면 덮어쓰지 않습니다.
cd /home/apple2ne1/projects/vibe-coding-platform
test -e .env.mysql && echo ".env.mysql이 이미 있습니다" || cp .env.mysql.example .env.mysql
이 문서의 공개 실습값을 사용하는 경우 .env.mysql에 다음 값을 입력하고 Ctrl+S로 저장합니다.
MYSQL_DATABASE=vibe_platform
MYSQL_USER=vibe_app
MYSQL_PASSWORD=vibe-mysql-04-demo
MYSQL_ROOT_PASSWORD=vibe-mysql-root-04-demo

MYSQL_ROOT_PASSWORD는 MySQL 컨테이너를 초기화하기 위한 실습용 루트 암호입니다. 백엔드는 루트 계정으로 접속하지 않고 MYSQL_USER에 지정한 vibe_app 사용자로 접속합니다. 이 값들은 공개된 로컬 실습 전용 값이므로 운영 환경과 다른 프로젝트에서 재사용하지 않습니다.
파일 내용을 출력하지 않고 저장 여부와 Git 제외 상태만 확인합니다.
test -s .env.mysql && echo "MySQL 환경 파일이 저장되었습니다" || echo "MySQL 환경 파일이 없거나 비어 있습니다"
git check-ignore -v .env.mysql
백엔드용 backend/.env 준비
Codex가 backend/.env 준비를 요청하면 다음 절차를 수행합니다. backend/.env는 실제 데이터베이스 암호와 관리자 Seed 값을 저장하는 로컬 전용 파일이므로 내용을 Codex 대화, 문서, 화면 캡처와 Git에 포함하지 않습니다.

Codex가 프로젝트를 확인한 뒤 backend/.env가 없다고 보고하면 환경 파일 준비 요청에 따라 다음 절차를 진행합니다.
먼저 프로젝트 루트로 이동하고 기존 backend/.env가 있는지 확인합니다.
방법 1: VS Code 작업
VS Code의 Explorer에서 backend/.env.example을 마우스 오른쪽 버튼으로 선택하고 Copy를 선택합니다.

backend 폴더를 마우스 오른쪽 버튼으로 선택하고 Paste를 선택합니다.

생성된 .env copy.example 파일을 마우스 오른쪽 버튼으로 선택하고 Rename을 선택합니다.

파일 이름을 .env로 입력하고 Enter를 누릅니다.

방법 2: 터미널 작업
cd /home/apple2ne1/projects/vibe-coding-platform
test -e backend/.env && echo "backend/.env가 이미 있습니다" || echo "backend/.env를 새로 만들 수 있습니다"
backend/.env가 이미 있습니다가 표시되면 다음 복사 명령을 실행하지 않습니다. 기존 파일을 덮어쓰지 말고 VS Code에서 열어 필요한 변수만 확인합니다.
backend/.env를 새로 만들 수 있습니다가 표시되면 예시 파일을 복사합니다.
cp backend/.env.example backend/.env
VS Code의 Explorer에서 backend/.env를 열고 예시 파일에 준비된 다음 변수의 값을 로컬 환경에 맞게 입력합니다.
MYSQL_HOSTMYSQL_PORTMYSQL_DATABASEMYSQL_USERMYSQL_PASSWORDADMIN_SEED_EMAILADMIN_SEED_PASSWORD
backend/.env를 새로 만들었다면 다음 코드 블록의 변수를 입력합니다. 기존 backend/.env가 있다면 파일 전체를 교체하지 말고 아래와 이름이 같은 변수만 추가하거나 값을 수정하며, 다른 단계에서 사용하던 변수와 설정은 그대로 보존합니다. 이미지에 표시된 replace-with-a-dedicated-database-password와 replace-with-a-strong-admin-password는 설명용 문구이므로 그대로 사용하지 않습니다.
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_DATABASE=vibe_platform
MYSQL_USER=vibe_app
MYSQL_PASSWORD=vibe-mysql-04-demo
ADMIN_SEED_EMAIL=admin@example.test
ADMIN_SEED_PASSWORD=vibe-admin-04-demo

새 파일에는 코드 블록 전체를 복사하여 붙여넣고, 기존 파일에는 필요한 변수만 반영한 뒤 Ctrl+S로 저장합니다. 파일 이름은 이미지에 보이는 .env copy.example이 아니라 정확히 backend/.env여야 합니다.
MYSQL_DATABASE, MYSQL_USER와 MYSQL_PASSWORD는 개발용 MySQL을 초기화할 때 사용한 데이터베이스 이름, 사용자와 암호에 각각 일치해야 합니다. 특히 MySQL 쪽 사용자 암호와 backend/.env의 MYSQL_PASSWORD가 다르면 백엔드가 데이터베이스에 연결되지 않습니다. Codex가 MySQL용 환경 파일에도 같은 실습값을 입력하라고 안내하면 실제 구현에서 확인한 파일과 변수 이름에 위 데이터베이스명·사용자·암호를 동일하게 적용합니다.
직접 만든 별도의 로컬 값을 사용하는 경우에는 변수 이름과 =는 유지하고 오른쪽 값만 바꿉니다. 별도로 만든 실제 암호와 관리자 Seed 값은 문서, 화면 캡처, 터미널 명령 인자 또는 Codex 대화에 입력하지 않습니다.
저장한 뒤 파일 내용을 출력하지 않고 파일 존재 여부와 Git 제외 상태만 확인합니다.
test -s backend/.env && echo "환경 파일이 저장되었습니다" || echo "환경 파일이 없거나 비어 있습니다"
git check-ignore -v backend/.env
첫 번째 명령에서 환경 파일이 저장되었습니다가 표시되고, 두 번째 명령에서 .gitignore 규칙과 backend/.env 경로가 표시되어야 합니다. 검사 명령은 환경변수 값을 출력하지 않습니다.
환경 파일을 만드는 것만으로 Node.js가 자동으로 읽는 것은 아닙니다. backend/package.json의 dev, start와 seed:admin이 모두 같은 backend/.env를 읽는지 확인합니다.
cd /home/apple2ne1/projects/vibe-coding-platform/backend
npm pkg get scripts.dev scripts.start scripts.seed:admin
node -e 'const s=require("./package.json").scripts||{}; const n=["dev","start","seed:admin"]; const bad=n.filter(k=>typeof s[k]!=="string"||!s[k].includes("--env-file=.env")); if(bad.length){console.error("확인 필요: "+bad.join(", ")); process.exit(1)} console.log("환경 파일 로딩 스크립트 확인 완료")'

첫 번째 명령에서는 세 스크립트의 실제 구성을 확인합니다. 두 번째 명령에서 환경 파일 로딩 스크립트 확인 완료가 표시되어야 합니다. 확인 필요가 표시되면 환경 파일 준비 완료를 전달하거나 시드·백엔드 실행으로 넘어가지 않고, 표시된 스크립트 이름을 Codex에 전달하여 수정합니다.
환경 파일 저장, Git 제외와 세 스크립트의 환경 파일 로딩을 모두 확인한 뒤 같은 Codex CLI 대화에 다음 문장만 전달합니다.
환경 파일 준비 완료

Codex는 이 응답을 받으면 실제 구현 방식에 따라 개발용 MySQL 실행, 스키마 적용, 관리자 Seed와 데이터베이스·API 검사를 계속합니다. Codex가 다음 사용자 작업을 안내하면 한 번에 한 단계씩 수행하고 실제 비밀정보는 전달하지 않습니다.
Codex가 WebUI 확인으로 이동 가능이라고 보고한 경우에만 다음 단계로 진행합니다. 환경 파일 입력이나 오류 복구가 필요하면 Codex가 안내한 한 가지 작업을 수행하고 결과를 같은 대화에 전달합니다.
2. 데이터베이스와 API 확인 결과 읽기
Codex의 보고에서는 다음 항목을 확인합니다.
- Backup 파일명 또는 저장 위치, 생성 시각, 대상 데이터베이스와 무결성 확인값이 보고되었습니다.
- 원본 데이터베이스와 이름 있는 볼륨을 건드리지 않는 격리된 대상에서 Restore가 검증되었고, 스키마와 핵심 데이터 건수가 대조되었습니다.
- Schema·Seed 적용 전후의 대상 데이터베이스,
users테이블과 기존 행 수가 비교되었으며 복구 범위가 보고되었습니다. - MySQL이 문서의 기준인 로컬 전용 주소로 실행되었습니다.
- 데이터 저장 위치 또는 이름 있는 볼륨이 확인되었습니다.
- 실제 스키마 파일을 통해
users테이블과id,email,password_hash,role,is_active열이 준비되었습니다. - 실제 Seed 스크립트가 환경변수로 관리자 이메일과 암호를 받아 관리자 행을 중복 없이 준비했습니다.
- 저장된 암호는 평문이 아니라 Argon2id 암호 해시이며 실제 값은 출력되지 않았습니다.
- 관리자 성공과 잘못된 암호, 없는 사용자, 비활성 사용자와 일반 사용자의 실패 결과가 구분되었습니다.
위 항목 가운데 하나라도 실패하거나 미수행이면 WebUI 결과만으로 04단계를 완료하지 않습니다. 오류와 Codex의 마지막 보고를 같은 대화에 전달해 복구한 뒤 다시 확인합니다.
3. MySQL, 백엔드와 프런트엔드 실행
데이터베이스와 Seed 준비가 끝나면 WebUI 확인에 필요한 MySQL, 백엔드와 프런트엔드를 순서대로 실행합니다. 먼저 프로젝트 루트에서 개발용 MySQL 서비스의 현재 상태를 확인한 뒤 실행합니다.
cd /home/apple2ne1/projects/vibe-coding-platform
docker compose ps -a mysql
docker compose up -d mysql
docker compose ps mysql
첫 번째 docker compose ps -a mysql 결과에서 MySQL 서비스가 exited 또는 created 상태인지, 이미 실행 중인지 확인합니다. 컨테이너를 삭제하거나 볼륨을 초기화하지 않습니다. docker compose up -d mysql은 서비스를 생성하거나 시작하며, 기존 컨테이너의 서비스 설정 또는 이미지가 달라졌다면 컨테이너를 재생성할 수 있습니다. 이때 Compose에 연결된 이름 있는 볼륨은 보존되지만, 실행 중인 컨테이너 구성이 반드시 그대로 유지되는 것은 아닙니다. 기존에 생성된 컨테이너를 구성 변경 없이 다시 시작하려는 경우에는 Codex가 실제 상태를 확인한 뒤 docker compose start mysql 사용 여부를 안내하도록 합니다.
docker compose ps mysql 결과에서 MySQL 서비스가 실행 중이고 healthy로 표시되어야 합니다. 처음 실행하여 health: starting 또는 starting으로 표시되면 잠시 기다린 뒤 다음 명령으로 다시 확인합니다.

MySQL이 healthy가 되지 않거나 exited, unhealthy 또는 오류가 표시되면 백엔드·프런트엔드 실행과 WebUI 확인으로 넘어가지 않습니다. 다음 명령의 출력에서 실제 암호를 제외한 오류 부분을 Codex에 전달하여 해결합니다.
docker compose logs --tail=100 mysql
MySQL이 healthy인 것을 확인한 뒤 VS Code에서 터미널을 두 개 열어 백엔드와 프런트엔드를 각각 실행합니다.
# 터미널 1
cd /home/apple2ne1/projects/vibe-coding-platform/backend
npm run dev
# 터미널 2
cd /home/apple2ne1/projects/vibe-coding-platform/frontend
npm run dev

백엔드에서 MySQL 연결 오류가 표시되거나 프런트엔드 주소가 열리지 않으면 웹 브라우저 검사로 넘어가지 않습니다. 오류 출력 전체를 Codex에 전달하여 원인을 확인하고 복구합니다.
4. WebUI와 Network 확인
이 검사는 공통 WebUI Gate의 04단계 실제 웹 브라우저 확인입니다. Backup·Restore, MySQL 스키마·Seed·연결 검사가 통과했더라도 이 화면 검사를 수행하지 않으면 04단계를 완료한 것으로 기록하지 않습니다.
WebUI 확인을 시작하기 전에 MySQL이 healthy 상태이고, 관리자 Seed가 완료되었으며, users 테이블의 관리자 행이 admin, 활성 상태로 조회되는지 확인합니다.
Windows 관리 PC의 웹 브라우저에서 http://서버-IP:5173에 접속하고 다음 순서로 확인합니다.
여기에서는 http://192.168.1.231:5173 으로 접속합니다.
- 공개 실습용 관리자 이메일
admin@example.test와 암호vibe-admin-04-demo로 로그인합니다.

- 로딩 표시 뒤 관리자 대시보드와 성공 메시지가 표시되는지 확인합니다.

- 개발자 도구의
Network에서/api/auth/login요청이 성공 상태로 응답하는지 확인합니다. - 암호를
wrong-password처럼 다른 값으로 바꾸어 로그인하고, 계정 존재 여부를 구분하지 않는 오류가 표시되는지 확인합니다.
실패 처리를 확인할 때는 공개 실습용 관리자 이메일을 유지하고 암호만 wrong-password로 바꾸어 로그인합니다.

로그인이 거부되고 계정 존재 여부를 드러내지 않는 일반 오류 메시지가 표시되는지 확인합니다.

F12를 눌러 개발자 도구를 열고, 실패한/api/auth/login요청의 HTTP 상태와 오류 응답을Network에서 확인합니다.

- 성공·실패 응답에 암호,
password_hash와 데이터베이스 상세 오류가 없는지 확인합니다. - 웹 브라우저의
Console에 예상하지 못한 오류가 없는지 확인합니다.

- 페이지를 새로 고치면 로그인 상태가 사라지는 현재 단계의 제한을 확인합니다.
- 모바일 화면 크기에서 로그인 화면의 내용이 잘리거나 겹치지 않는지 확인하고, 개발자 도구의
Issues에 문제가 표시되지 않는지도 확인합니다.

확인한 결과는 다음처럼 구분하여 메모합니다.
- 성공: 관리자 로그인, 대시보드 표시, API 성공 응답
- 실패 처리 성공: 잘못된 암호 거부, 안전한 오류 메시지와 API 실패 응답
- 비밀정보 보호: 응답에 암호,
password_hash와 데이터베이스 상세 오류 없음 - 현재 제한 확인: 새로고침 뒤 로그인 상태가 사라짐
- 공통 Gate:
Console오류 없음, 데스크톱·모바일 화면과 키보드 조작 확인
WebUI가 열리지 않거나 로그인 결과가 데이터베이스·API 검사와 다르면 다른 검사의 통과 여부만으로 완료 처리하지 않습니다. 백엔드 터미널 오류와 웹 브라우저의 Console·Network 결과를 Codex에 전달하여 해결합니다.
로그인에 문제가 있을 때: 1차 검증
- MySQL 상태 확인
cd /home/apple2ne1/projects/vibe-coding-platform
docker compose ps mysql
healthy 상태인지 확인합니다.
- 관리자 시드 다시 적용
cd /home/apple2ne1/projects/vibe-coding-platform/backend
npm run seed:admin
반드시 성공 메시지가 표시되어야 합니다. Admin seed failed가 표시되면 로그인 확인으로 넘어가지 않습니다.
- 관리자 행 확인
프로젝트 루트에서 다음 명령을 실행합니다.
cd /home/apple2ne1/projects/vibe-coding-platform
docker compose exec mysql mysql \
-u root -p \
-e "USE vibe_platform; SELECT id, email, role, is_active FROM users WHERE email='admin@example.test';"
암호 입력을 요구하면 .env.mysql의 MYSQL_ROOT_PASSWORD 값 vibe-mysql-root-04-demo를 입력합니다. 관리자 계정이 정상적으로 준비되었다면 다음과 같은 결과가 표시됩니다.
+----+--------------------+-------+-----------+
| id | email | role | is_active |
+----+--------------------+-------+-----------+
| 1 | admin@example.test | admin | 1 |
+----+--------------------+-------+-----------+
판정 기준은 다음과 같습니다.
- 관리자 행이 1개 표시됨
email:admin@example.testrole:adminis_active:1
id는 시드 실행 환경에 따라 1이 아닌 다른 숫자여도 정상입니다. 행이 출력되지 않으면 관리자 시드가 적용되지 않았거나 다른 데이터베이스를 조회한 것입니다.

—-
5. 실습 프로세스 중지
WebUI 확인이 끝나면 프런트엔드, 백엔드와 MySQL 순서로 중지합니다.
프런트엔드를 실행한 터미널을 선택하고 다음 중 한 가지 방법으로 Vite를 중지합니다.
q를 입력한 뒤 Enter
또는 해당 터미널에서 Ctrl+C를 누릅니다. 셸 프롬프트가 다시 표시되면 프런트엔드가 중지된 것입니다.
백엔드를 실행한 터미널을 선택하고 다음 키를 누릅니다.
Ctrl+C
Backend listening on http://127.0.0.1:3000 아래에 셸 프롬프트가 다시 표시되면 백엔드의 tsx watch 프로세스가 중지된 것입니다.

별도의 터미널에서 3000번과 5173번 포트의 수신 프로세스가 남아 있지 않은지 확인합니다.
if ss -ltn | grep -Eq ':(3000|5173)\b'; then
echo "확인 필요: 3000 또는 5173 포트의 프로세스가 아직 실행 중입니다"
else
echo "백엔드와 프런트엔드가 중지되었습니다"
fi

확인 필요가 표시되면 다른 프로세스를 임의로 강제 종료하지 않습니다. VS Code의 터미널 목록에서 실행 중인 백엔드·프런트엔드 터미널을 찾아 위 종료 키를 다시 사용합니다.
백엔드와 프런트엔드가 중지된 것을 확인한 뒤 MySQL을 다음 명령으로 중지합니다.
cd /home/apple2ne1/projects/vibe-coding-platform
docker compose stop mysql

docker compose stop mysql은 MySQL 서비스의 컨테이너를 중지하지만 이름 있는 볼륨의 데이터는 유지합니다. 다음 실습에서 같은 스키마와 Seed 데이터를 다시 사용할 수 있습니다. 볼륨을 삭제하는 docker compose down -v는 실행하지 않습니다.
6. Codex에 검증 결과 기록 요청
사용자가 위 절차의 WebUI 또는 대체 검사를 마친 뒤에는 여러 기록 파일을 직접 열어 맞추지 않습니다. 검증 결과가 성공이든 실패든 다음 Prompt 하나를 Codex CLI에 전달하여 확인된 결과와 단계 상태를 정리합니다.
04단계 구현과 사용자 WebUI 확인을 앞 절차에 따라 수행했어. 현재 작업 기록과 실제 결과를 확인하되, 완료를 미리 전제하지 말고 필수 근거가 모두 있는지 판정해 줘.
Schema·Seed 적용 전에 Backup 파일명 또는 저장 위치, 생성 시각, 대상 데이터베이스와 무결성 확인값이 기록되었는지 확인해 줘. 원본과 분리된 대상의 Restore 결과, 스키마와 핵심 데이터 건수, 적용 전후 대상 데이터베이스·users 테이블·기존 행 수 비교 근거도 확인해 줘. 이 가운데 하나라도 없거나 실패했다면 04단계를 완료로 기록하지 말고 Schema·Seed와 WebUI 결과만으로 통과 처리하지 마.
webui-gate.md에는 MySQL Login 검증 결과를, local-development.md에는 MySQL 준비와 연결 확인 방법을, README.md에는 실제 Admin 검증 상태와 현재 제한을 기록해 줘. Database Schema·Seed 원본과 환경변수 예시는 실제 구현이 변경된 경우에만 맞춰 갱신하고 실제 암호는 기록하지 마.
실행하지 않은 검사는 통과로 기록하지 말고 기존 기록을 삭제하거나 덮어쓰지 마.
현재 코드와 확인된 결과를 기준으로 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의 완료 보고에서 변경한 파일, 실제 검증 기록, 남은 제한과 다음 단계 진행 가능 여부를 확인합니다.

7. 최종 변경 검토와 커밋
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


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

문서·검증만 수행한 단계라면 Codex가 완료 보고에 제시한 docs:, test: 또는 chore: 메시지를 사용합니다. 필수 검사가 실패하면 커밋으로 완료 상태를 만들거나 다음 단계 작업 지시서를 실행하지 않습니다.
정식 작업 지시서 해설
이 절은 새로운 작업을 지시하거나 추가 명령을 실행하는 단계가 아닙니다. 정식 작업 지시서 실행, 구현 결과 검증, Codex의 검증 기록 갱신과 커밋까지 마친 뒤 읽는 복습용 해설입니다.
앞에서 수행한 요구사항의 목적, 데이터 흐름, 보안 경계와 완료 판정 기준을 차례로 되짚습니다. 아래 파일명은 이 실습의 실제 구현에서 확인한 구성입니다. 다른 프로젝트에 그대로 적용해야 하는 공통 이름이 아니며, 별도의 실행 안내가 없는 한 명령을 다시 실행하지 않습니다.
1. 04단계가 바꾼 로그인 판정 기준
03단계에서는 프런트엔드와 Express API의 연결 흐름을 임시 관리자 값으로 확인했습니다. 04단계에서는 이 임시 판정을 제거하고, 백엔드가 MySQL의 users 테이블에서 사용자를 조회하여 저장된 계정 정보로 관리자 로그인 가능 여부를 판정하도록 바꿨습니다. 프런트엔드의 요청·응답 계약과 화면 흐름은 유지했으므로, 이번 단계의 중심은 화면 변경이 아니라 데이터베이스 연결과 인증 판정 교체입니다.
개발용 MySQL만 compose.yaml의 서비스로 추가했습니다. MySQL 포트는 서버의 127.0.0.1:3306에만 게시하여 다른 PC에서 직접 접속하지 못하게 했고, 데이터는 이름 있는 볼륨에 저장하여 컨테이너를 중지해도 유지되게 했습니다. 프런트엔드와 백엔드의 컨테이너화는 08단계 범위이므로 이번 단계에는 포함하지 않았습니다.
04단계에서 채워진 주요 폴더와 파일
vibe-coding-platform/
├── docs/
│ └── work-orders/
│ └── 04-mysql-admin-account.md
├── backend/
│ ├── .env.example
│ ├── package.json
│ └── src/
│ ├── app.ts
│ ├── database.ts
│ └── seed-admin.ts
├── database/
│ └── schema.sql
└── compose.yaml
database/schema.sql은 MySQL 구조의 원본이고 backend/src/seed-admin.ts는 공개 실습용 관리자 행을 중복 없이 준비합니다. 실제 암호와 연결 문자열은 .env.example이나 Git에 기록하지 않습니다.
2. 데이터 변경 전에 Backup과 Restore를 검증하는 이유
users 테이블 생성과 관리자 Seed는 영속 데이터에 영향을 주는 작업입니다. 이름 있는 볼륨은 컨테이너를 중지하거나 다시 만들어도 데이터를 유지하지만, 그 자체가 Backup은 아닙니다. 잘못된 스키마 적용이나 데이터 변경까지 그대로 유지될 수 있기 때문입니다.
따라서 작업 지시서는 변경 전에 현재 데이터베이스와 기존 데이터 상태를 읽기 전용으로 확인하고, 실제 프로젝트 또는 현재 환경에서 검증된 방법으로 Backup을 식별하도록 요구합니다. Backup은 존재만 확인해서는 충분하지 않습니다. 원본과 분리된 대상에 Restore한 뒤 스키마와 핵심 데이터 건수를 확인해야 복구 가능성을 판단할 수 있습니다.
검증된 절차가 없거나 격리 Restore가 실패하면 스키마와 Seed를 적용하지 않습니다. 이때 Codex는 확인하지 않은 명령을 만들어 실행하지 않고 중단 이유를 보고합니다. 장애가 발생해도 실제 Restore, 볼륨 교체나 데이터 삭제는 복구 대상과 데이터 손실 가능성을 설명한 뒤 사용자의 명시적 승인을 받아야 합니다.
3. 실제 구현 파일과 구성 요소의 역할
compose.yaml은 개발용 MySQL 서비스, 로컬 전용 포트 게시, 상태 검사와 이름 있는 볼륨을 정의합니다..env.mysql.example은 MySQL 컨테이너 초기화에 필요한 변수 이름만 보여 줍니다. 실제 로컬 값은 Git에서 제외되는.env.mysql에 둡니다.database/schema.sql은users테이블의id,email,password_hash,role,is_active열과 이메일 중복 방지 조건을 정의합니다.backend/src/database.ts는mysql2Promise Pool을 만들고 백엔드가 MySQL 연결을 재사용하게 합니다.backend/src/seed-admin.ts는 환경 변수로 받은 관리자 이메일과 암호를 사용해 학습용 관리자 행을 준비합니다. 같은 Seed를 다시 실행해도 관리자 행이 중복되지 않아야 합니다.backend/src/app.ts는 이메일을 Prepared Statement로 조회하고, 역할·활성 상태와 암호를 확인한 뒤 로그인 결과를 반환합니다.backend/package.json의dev,start,seed:admin은 모두--env-file=.env를 사용하여 같은backend/.env를 읽습니다.
MySQL 컨테이너 초기화용 .env.mysql과 백엔드 연결용 backend/.env는 역할이 다릅니다. 초기화 관리 계정과 애플리케이션 연결 계정을 구분하고, 백엔드는 필요한 데이터베이스 권한만 가진 애플리케이션 계정을 사용해야 합니다. 두 환경 파일의 데이터베이스명, 애플리케이션 사용자와 암호는 서로 일치해야 하지만 실제 값은 Git, 문서, 명령 인자와 로그에 남기지 않습니다. 문서의 인증값은 운영 환경에서 재사용하지 않는 공개 실습 예시입니다.
4. 로그인과 암호 검증이 동작하는 원리
관리자 Seed는 입력받은 암호를 평문으로 저장하지 않고 Argon2id 암호 해시로 바꾸어 password_hash에 저장합니다. 로그인 요청이 오면 백엔드는 입력된 이메일로 사용자 한 명을 조회하고, 입력 암호와 저장된 해시를 비교합니다.
암호가 맞더라도 role이 admin이 아니거나 is_active가 활성 상태가 아니면 관리자 로그인을 허용하지 않습니다. 존재하지 않는 사용자와 잘못된 암호에는 같은 형태의 오류를 반환하여 이메일의 등록 여부를 추측하기 어렵게 합니다. API 응답과 로그에는 입력 암호, 전체 password_hash와 데이터베이스 상세 오류를 넣지 않습니다.
Prepared Statement를 사용하는 이유는 이메일 값을 SQL 문자열에 직접 이어 붙이지 않고 별도 값으로 전달하기 위해서입니다. Promise Pool은 요청마다 새 연결을 무조건 만들지 않고 준비된 연결을 재사용하도록 돕습니다.
5. --env-file=.env가 필요한 이유
backend/.env 파일을 만드는 것만으로 Node.js가 그 파일을 자동으로 읽는 것은 아닙니다. seed:admin만 환경 파일을 읽거나 개발 서버만 다른 설정을 읽으면, Seed는 성공했는데 실제 로그인은 다른 데이터베이스를 조회하는 문제가 생길 수 있습니다.
따라서 dev, start와 seed:admin 세 스크립트가 모두 node --env-file=.env로 같은 파일을 읽게 했습니다. package.json을 수정하기 전에 실행한 백엔드 프로세스에는 변경된 설정이 반영되지 않으므로, 기존 프로세스를 중지하고 다시 실행해야 합니다.
6. 변경 전·후 Gate와 완료 검사의 의미
변경 전 WebUI Gate는 03단계의 화면과 API가 정상인 기준점을 남깁니다. 변경 후 같은 Gate를 다시 실행하면 04단계 변경으로 기존 화면 흐름이 깨지지 않았는지 비교할 수 있습니다. 여기에 데이터베이스와 API 검사를 더하여 한 종류의 결과만으로 완료를 판정하지 않습니다.
- 백엔드 형식 검사와 단위 테스트는 로그인 분기와 민감정보 비노출 조건을 확인합니다.
- 프런트엔드 빌드는 기존 API 계약과 화면 코드가 정상적으로 빌드되는지 확인합니다.
- MySQL 검사는 서비스 상태, 로컬 전용 포트,
users테이블의 필수 열과 관리자 행을 확인합니다. - Seed 재실행 검사는 같은 이메일의 관리자 행이 중복 생성되지 않는지 확인합니다.
- 실제 API 검사는 관리자 성공과 잘못된 암호, 없는 사용자, 비활성 사용자 및 일반 사용자의 거부를 확인합니다.
- WebUI 검사는 실제 웹 브라우저에서 로그인 성공 화면, 안전한 실패 메시지,
Network응답,Console오류 여부와 모바일 화면을 확인합니다.
자동 검사만 통과하고 웹 브라우저 확인을 수행하지 않았다면 공통 WebUI Gate는 완료되지 않습니다. 반대로 화면만 전환되더라도 실제 MySQL 연결, 스키마와 Seed 검사가 실패했다면 04단계를 완료할 수 없습니다.
7. 정식 요구사항과 구현에서 확인한 보강 항목
정식 작업 지시서의 필수 기준은 users 테이블의 핵심 열, MySQL 조회, Argon2id 검증, 역할·활성 상태 판정, 민감정보 비노출과 지정된 검사입니다. 실제 구현에서 확인한 이메일 UNIQUE 제약, 반복 실행해도 중복 행을 만들지 않는 Seed와 MySQL 상태 검사는 이 기준을 안전하게 만족하기 위한 보강 항목입니다.
이 구분이 중요한 이유는 실제로 확인한 구현을 정식 작업 지시서에 없던 보편적 요구사항처럼 과장하지 않기 위해서입니다. 반대로 보강 항목이 실제 프로젝트에 없다면 해설만 보고 구현되었다고 기록해서도 안 됩니다.
8. 이번 단계에서 의도적으로 제외한 범위
이번 단계는 로그인 자격 확인까지만 구현합니다. 세션을 만들지 않으므로 새로고침하면 로그인 상태가 사라집니다. Redis, Nginx, /api/auth/me, 세션을 종료하는 Logout API와 보호된 관리자 API는 이번 단계에서 추가하지 않았습니다. 이 항목들은 오류나 누락이 아니라 05단계 이후에 구현할 경계입니다.
다만 앞 단계에서 만든 프런트엔드의 Logout 버튼과 로컬 화면 상태 초기화 동작은 기존 화면 흐름이므로 제거하지 않습니다. 이번 단계에서 제외한 것은 서버 세션을 폐기하는 Logout 기능입니다.
9. 검증 기록을 구현과 분리한 이유
정식 작업 지시서는 구현 범위와 완료 조건의 기준으로 유지합니다. 사용자가 WebUI 확인까지 마친 뒤에는 별도의 6. Codex에 검증 결과 기록 요청을 실행하여, 실제로 확인한 결과만 docs/development-guides/webui-gate.md, docs/development-guides/local-development.md, README.md와 실제 변경된 관련 문서에 기록합니다.
실패하거나 수행하지 않은 검사를 통과로 바꾸지 않으며, 요구사항 자체를 검증 결과에 맞추어 임의로 낮추지도 않습니다. 이 분리 덕분에 구현 지시와 실제 수행 기록이 섞이지 않고, 여러 문서의 04단계 상태를 같은 기준으로 맞출 수 있습니다.
local-development.md에는 .env.mysql과 backend/.env 준비 위치, MySQL 시작·상태 확인·중지, 스키마와 Seed 적용 순서를 기록합니다. webui-gate.md에는 데이터베이스·API·웹 브라우저에서 실제로 통과하거나 실패한 결과를 기록하고, README.md에는 현재 완료 단계와 다음 단계로 넘어갈 수 있는지를 요약합니다.
10. README.md에 남는 완료 상태의 의미
검증을 마친 뒤 루트 README.md에는 다음과 같은 단계 상태가 기록됩니다.
## 현재 단계
04단계: MySQL Admin 계정 연결하기
## 완료된 기능
- `127.0.0.1:3306`으로 제한한 개발용 MySQL
- 이름 있는 볼륨에 저장되는 데이터베이스 데이터
- `users` 테이블과 이메일 `UNIQUE` 제약
- Argon2id 암호 해시를 사용하는 관리자 Seed
- Prepared Statement 기반 사용자 조회와 실제 관리자 로그인
## 보안 기준
- 실제 암호와 `password_hash`를 API 응답, 로그와 문서에 기록하지 않습니다.
- 관리자, 일반 사용자와 비활성 사용자를 구분합니다.
## 현재 제한
- 세션은 아직 적용하지 않아 새로고침하면 로그인 상태가 사라집니다.
이 예시는 실제 검사와 문서 갱신이 모두 끝났을 때만 사용할 수 있습니다. Backup 식별과 격리 Restore, 데이터 적용 전후 비교 또는 필수 Gate가 실패·미수행이면 완료 상태나 다음 단계 진행 가능으로 기록하지 않습니다.
11. 다음 단계
다음 글에서는 로그인 성공 시 세션을 만들고 /api/auth/me와 보호된 관리자 경로를 구현합니다.
참고 자료 및 출처
- MySQL 8.4 CREATE TABLE: 테이블, 키와 열 정의 확인. 확인일: 2026-08-21
- MySQL2 Documentation: Promise Wrapper와 Pool 기능 확인. 확인일: 2026-08-21
- MySQL2 Prepared SELECT:
execute()와 Placeholder 사용 확인. 확인일: 2026-08-21 - MySQL 공식 Docker 이미지 문서: 기존 데이터 폴더가 있을 때 초기화 환경 변수와 초기화 스크립트가 다시 적용되지 않는 조건 확인. 확인일: 2026-08-21
- Docker Compose up: 서비스 생성·시작과 설정 또는 이미지 변경 시 컨테이너 재생성 동작 확인. 확인일: 2026-08-21
- Docker Compose stop: 컨테이너를 제거하지 않고 서비스를 중지하며
start로 다시 실행할 수 있는 동작 확인. 확인일: 2026-08-21

