바이브 빌드 (Vibe Build)

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

8단계: 운영 구성을 차례로 추가하기

8단계: 운영 구성을 차례로 추가하기

Article Guide

목  차

핵심 요약

2~7단계에서 다음 개발 흐름을 완성했습니다.

웹 브라우저 :5173
        ↓
React 로그인
        ↓
Express API :3000
        ↓
MySQL 관리자 사용자
        ↓
개발용 세션
        ↓
보호된 `Admin Dashboard`
        ↓
Text Tool Menu

이번 단계에서는 애플리케이션 기능을 늘리지 않고 실행 환경만 다음 순서로 바꿉니다.

1. Docker Compose로 기존 서비스 통합
2. `MemoryStore`를 Redis 세션 저장소로 교체
3. React 배포용 빌드를 Nginx에서 제공
4. 외부 공개 포트를 Nginx 하나로 제한
5. HTTPS 적용을 위한 설정 준비

위 내용은 “개발용으로 따로 움직이던 여러 프로그램을 하나의 실제 서비스처럼 정리하는 작업” 입니다.

  1. Docker Compose로 기존 서비스 통합
  2. 지금까지 따로 실행하던 React, 백엔드, MySQL, Redis, Nginx 같은 프로그램을 compose.yaml 하나로 함께 실행하고 관리하도록 만드는 것입니다. 쉽게 말하면, 여러 개의 전원 스위치를 하나의 메인 스위치로 묶는 것입니다.

  1. MemoryStore를 Redis 세션 저장소로 교체
  2. 로그인 정보를 백엔드 프로그램의 임시 메모리에 저장하지 않고, Redis라는 별도의 저장 공간에 보관하도록 바꾸는 것입니다. 이렇게 하면 백엔드가 재시작되거나 여러 대가 되어도 로그인 상태를 더 안정적으로 관리할 수 있습니다.

  1. React 배포용 빌드를 Nginx에서 제공
  2. 개발할 때 사용하는 React 서버를 그대로 외부에 공개하지 않고, React를 실제 서비스용 파일로 만든 뒤 Nginx가 그 화면을 사용자에게 전달하도록 합니다. 즉,

사용자 → Nginx → React 화면

구조로 바꾸는 것입니다.

  1. 외부 공개 포트를 Nginx 하나로 제한
  2. 백엔드, MySQL, Redis 등의 포트를 인터넷에 직접 공개하지 않고, Nginx의 포트만 외부에 공개합니다. 예를 들어 외부에서는 80이나 443만 접속할 수 있게 하고, 나머지 서비스는 Docker 내부에서만 통신하게 합니다. 이렇게 하면 구조가 단순해지고 보안에도 유리합니다.

  1. HTTPS 적용을 위한 설정 준비
  2. 현재의 http:// 접속을 나중에 https://로 바꿀 수 있도록 Nginx와 도메인 구조를 미리 준비하는 것입니다. HTTPS를 적용하면 브라우저와 서버 사이의 통신이 암호화됩니다.

전체 흐름을 한 줄로 나타내면:

현재: 사용자 → HTTP/Nginx → React 또는 백엔드 → MySQL·Redis
향후: 사용자 → HTTPS/Nginx → React 또는 백엔드 → MySQL·Redis

한 번에 모두 변경하지 않습니다. 각 하위 작업이 정상임을 확인한 뒤 다음 구성을 추가합니다.

08단계에서는 Nginx 통합과 HTTPS 적용을 위한 구조만 준비합니다. 실제 도메인, 인증서와 secure 쿠키를 사용하는 HTTPS 검증은 15단계에서 수행하며, 확인하지 않은 HTTPS 구성을 완료로 기록하지 않습니다.

이 단계에서 만드는 Compose, Redis와 Nginx 구성은 기존 기능을 통합 환경에서 처음 실행하기 위한 1차 통합 기준선입니다. Redis 운영 안정성은 13단계, 개발·통합 Compose 환경 분리는 14단계, 실제 HTTPS 경계는 15단계에서 차례로 강화합니다. 따라서 08단계 완료를 최종 운영 구성 완료로 해석하지 않습니다.

Nginx가 React 정적 파일과 Express API 요청을 나누고 Express가 MySQL과 Redis에 연결되는 Docker Compose 구성

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

학습용 간단한 지시 설명서

07단계 기능을 바꾸지 말고 Compose → Redis 세션 저장소 → Nginx 순서로 통합해 줘.
각 하위 작업 뒤 로그인, 세션, Dashboard와 Text Tool 회귀 검사를 통과한 다음에만 다음 구성을 추가해 줘.
기존 MySQL 데이터를 보존하고 MySQL Health Check와 Named Volume을 적용해 줘.
최종적으로 Nginx 8080만 공개하고 백엔드, MySQL과 Redis 포트는 공개하지 마.
5173 개발 환경은 07단계 기준선 재사용·자동 검사·미수행을 구분하고, 8080 통합 환경은 실제 WebUI Gate 결과를 보고해 줘.

정식 작업 지시서

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

# 08단계 정식 작업 지시서

## 단계별 상세 지시
루트 AGENTS.md, 요구사항과 docs/work-orders/08-production-integration.md를 먼저 읽어 줘.
07단계 전체 Gate 통과와 현재 MySQL 데이터·볼륨 상태를 확인한다.
변경 전에 단계별 계획, 백업 방법, 변경 파일, 공개 포트와 검증 명령을 보고한다.

허용 범위는 통합에 필요한 compose.yaml, docker/, nginx/, backend/, frontend/ 빌드 설정과 환경 변수 예시다.
애플리케이션 기능, API URL, 관리자 권한과 UI를 변경하지 않는다.

하위 작업 A에서는 백엔드와 MySQL만 Compose로 옮긴다.
기존 MySQL 볼륨과 관리자 사용자를 보존하고 Health Check와 Named Volume을 적용한다.
새 볼륨으로 바꿔야 하면 먼저 백업·복원과 사용자 수를 검증한다.
A의 회귀 검사가 통과하기 전에는 Redis와 Nginx를 추가하지 않는다.

하위 작업 B에서는 `MemoryStore`만 Redis 세션 저장소로 교체한다.
세션에는 userId만 저장하고 비밀값과 Redis 키를 출력하지 않는다.
백엔드 재시작 후 세션 유지와 로그아웃을 확인한다.
B의 회귀 검사가 통과하기 전에는 Nginx를 추가하지 않는다.

하위 작업 C에서는 React 배포용 빌드를 Nginx에서 제공하고 /api/를 백엔드로 전달한다.
외부에는 8080만 공개하고 백엔드, MySQL과 Redis 포트를 공개하지 않는다.
React 경로 새로고침, 쿠키와 상대 API 경로를 검증한다.
이 단계에서는 Nginx 단일 진입점과 HTTPS 적용을 위한 구조만 준비한다. 실제 도메인, 인증서와 `secure` 쿠키를 사용하는 HTTPS 검증은 15단계 범위로 남기고 HTTPS 완료로 기록하지 않는다.

각 하위 작업 전·후 Gate와 관련 typecheck, build, compose config, health와 API 검사를 실행한다.
5173 개발 환경은 07단계 기준선 재사용, 자동 검사와 미수행을 구분한다. 8080 통합 환경의 전체 Gate는 실제 수행 결과를 통과, 실패와 미수행으로 구분한다. 검증 기록 문서 갱신은 사용자가 WebUI 확인을 마친 뒤 별도 프롬프트로 수행한다.
데이터 보존, 세션 또는 Gate가 실패하면 다음 하위 작업이나 09단계로 진행하지 않는다.

## 완료 보고 형식

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

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

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

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

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

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

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

2. 정식 작업 지시서 저장

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

mkdir -p docs/work-orders
touch docs/work-orders/08-production-integration.md

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

VS Code 편집기에 08단계 정식 작업 지시서 내용을 저장한 화면

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

test -s docs/work-orders/08-production-integration.md
git diff -- docs/work-orders/08-production-integration.md
VS Code 원격 터미널에서 저장한 08단계 작업 지시서의 내용과 변경 사항을 확인한 화면

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

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

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

cd /home/apple2ne1/projects/vibe-coding-platform
git status --short
test -s docs/work-orders/08-production-integration.md
git add -- docs/work-orders/08-production-integration.md
git diff --cached --check
git --no-pager diff --cached --name-only
git --no-pager diff --cached
git commit -m "docs: add 08 step work order"
VS Code 원격 터미널에서 08-production-integration.md만 스테이징하고 git diff --cached 검사 후 08단계 작업 지시서 기준점 커밋을 완료한 화면

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

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

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

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

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

루트 AGENTS.md, docs/requirements/admin-platform.md와
docs/work-orders/08-production-integration.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 초기화, 비밀값 입력, `sudo` 권한이나 사용자의 실제 WebUI 확인처럼
사용자 결정이나 조작이 꼭 필요한 일은 임의로 진행하지 마.
그 경우에는 사용자가 해야 할 행동 한 가지만 쉬운 문장으로 설명하고, 복사할 명령이 있으면 코드 블록 하나로 제공한 뒤 결과를 기다려 줘.

선행 조건이 해결되면 별도의 재요청을 기다리지 말고 정식 작업 지시서의 허용 범위만 구현해 줘.
제외 범위와 다음 단계 기능은 추가하지 마.
실행하지 못한 검사는 통과로 기록하지 말고 필수 검사 실패를 완료로 판정하지 마.

자동 검사가 끝나면 사용자가 직접 확인해야 할 WebUI 또는 대체 검사만 번호가 있는 짧은 목록으로 안내해 줘.
검증 결과 기록, 스테이징과 커밋은 사용자가 실제 확인을 마친 뒤 별도 프롬프트로 요청할 것이므로 지금 실행하지 마.

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

1. 자동으로 해결한 문제
2. 구현한 내용과 변경 파일
3. 통과·실패·미수행 검사
4. 사용자가 지금 확인할 항목
5. 다음 절차 진행 가능 여부
Codex CLI에 08단계 공통 프롬프트를 전달하고 현재 단계 기록과 구현 조건을 확인하도록 요청한 화면

Codex가 자동 확인과 구현을 진행하는 동안 사용자 조작이 필요한 문제가 발견되면 한 번에 한 가지 행동을 안내합니다. 다음 예시는 기존 Nginx 컨테이너가 8080 포트를 사용하고 있어 해당 컨테이너만 중지해 달라고 요청한 경우입니다.

Codex CLI가 8080 포트 충돌 원인을 확인하고 기존 Nginx 컨테이너 중지를 요청한 화면

Codex CLI를 종료하지 않고 명령을 실행하려면 VS Code 상단 메뉴에서 Terminal → Split Terminal을 선택하여 같은 프로젝트의 터미널을 하나 더 엽니다.

VS Code에서 Codex CLI를 유지한 채 명령을 실행하기 위해 분할 터미널을 연 화면

새 터미널에서 Codex가 지정한 컨테이너 이름을 그대로 사용하여 안내받은 명령 하나만 실행합니다.

docker stop nginx-node-react-minimal-nginx-1
VS Code의 새 터미널에서 8080 포트를 사용하던 기존 Nginx 컨테이너를 중지한 화면

명령을 실행했는데 아무것도 출력되지 않으면 성공이라고 추측하지 않습니다. 같은 Codex CLI 대화에 아무것도 출력되지 않았어.라고 실제 결과를 전달합니다.

Codex CLI에 컨테이너 중지 명령의 출력이 없었다는 실제 결과를 전달한 화면

자동 검사가 끝나면 Codex가 사용자가 웹 브라우저에서 확인할 주소와 항목, 검증 기록·스테이징·커밋을 아직 실행하지 않았다는 사실을 구분하여 보고합니다.

Codex CLI가 08단계 구현과 자동 검사 결과 및 사용자가 확인할 항목을 보고한 화면

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

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

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

구현 결과 WebUI 실행과 확인

정식 작업 지시서로 구현이 끝났다면 전체 Compose 환경을 한 번에 실행하고 8080 통합 화면에서 기능을 확인합니다. 문제가 발생한 경우에만 증상에 맞는 A, B 또는 C 진단 프롬프트를 Codex CLI에 전달합니다.

전체 Compose 실행

cd /home/apple2ne1/projects/vibe-coding-platform
docker compose config
docker compose up -d --build
docker compose ps
docker compose logs --tail 100 backend mysql redis nginx
VS Code 원격 터미널에서 docker compose config로 전체 Compose 구성을 확인한 화면

백엔드, MySQL, Redis와 Nginx가 실행 또는 정상 상태이고 로그에 반복되는 연결 오류가 없는지 확인합니다. 민감한 값이 로그에 보이면 화면을 공개하지 말고 값이 출력되었다는 사실만 Codex에 전달합니다.

서비스가 정상 상태이면 Windows 관리 PC의 웹 브라우저에서 http://192.168.1.231:8080에 접속합니다. 5173의 Vite 개발 서버는 별도로 실행하지 않습니다.

WebUI 통합 확인

WebUI를 확인하기 전에 앞에서 실행한 전체 Compose 구성, 빌드, 서비스 상태와 로그 결과가 다음과 같이 정상인지 확인합니다.

VS Code 원격 터미널에서 docker compose up 명령으로 백엔드와 Nginx 이미지를 빌드하고 전체 서비스를 시작한 화면
VS Code 원격 터미널에서 docker compose ps로 백엔드와 MySQL 및 Nginx와 Redis의 실행 상태와 공개 포트를 확인한 화면
VS Code 원격 터미널에서 전체 Compose 서비스 로그를 확인한 화면
  1. 로그인하고 관리자 Dashboard를 엽니다.

admin@example.test vibe-admin-04-demo

위 이메일과 암호는 운영 환경에서 재사용할 수 없는 공개 실습용 예시입니다.

Windows 관리 PC의 웹 브라우저에서 Nginx 8080 주소의 관리자 로그인 화면에 공개 실습용 계정 정보를 입력한 화면
  1. Text Tool에서 짧은 문장을 실행합니다.
Nginx 8080 통합 화면의 Text Tool에서 짧은 문장을 실행하고 글자 수와 단어 수 결과를 확인한 화면
  1. /admin/admin/text-tool을 직접 열거나 새로고침해도 화면이 유지되는지 확인합니다.
  2. 로그인 상태에서 다음 명령으로 백엔드만 재시작합니다.
cd /home/apple2ne1/projects/vibe-coding-platform
docker compose restart backend
docker compose ps backend redis
VS Code 원격 터미널에서 백엔드 컨테이너를 재시작하고 백엔드와 Redis가 정상 상태인지 확인한 화면
  1. 백엔드가 다시 실행 또는 정상 상태가 된 뒤 Dashboard를 한 번 새로고침합니다.
  2. 로그인 상태와 Dashboard가 유지되는지 확인하고 Text Tool을 다시 실행합니다.
백엔드 재시작 뒤에도 로그인 상태가 유지되어 Nginx 8080의 Text Tool 화면을 계속 사용할 수 있는 화면
  1. 로그아웃한 뒤 관리자만 접근할 수 있는 주소를 직접 열어 로그인 화면으로 이동하는지 확인합니다.
  • http://192.168.1.231:8080/admin
  • http://192.168.1.231:8080/admin/text-tool
로그아웃한 뒤 관리자 주소를 직접 열었을 때 Nginx 8080의 로그인 화면으로 이동한 결과

재시작 직후에는 일시적인 연결 오류가 발생할 수 있습니다. 백엔드가 정상 상태로 돌아온 뒤 한 번만 다시 확인하며, 반복 새로고침으로 실패를 숨기지 않습니다.

공개 포트 확인

cd /home/apple2ne1/projects/vibe-coding-platform
docker compose ps
docker compose port nginx 8080
VS Code 원격 터미널에서 Nginx의 컨테이너 포트 8080이 호스트의 모든 IPv4 인터페이스 포트 8080에 게시된 결과를 확인한 화면

PORTS 열에서 Nginx만 호스트의 8080에 연결되고 백엔드, MySQL과 Redis에는 호스트 공개 포트가 없어야 합니다. Windows 관리 PC에서도 다음 명령으로 확인합니다.

Test-NetConnection 192.168.1.231 -Port 8080
Test-NetConnection 192.168.1.231 -Port 3000
Test-NetConnection 192.168.1.231 -Port 3306
Test-NetConnection 192.168.1.231 -Port 6379
Windows 관리 PC에서 Nginx 8080 포트에 접속할 수 있음을 확인한 PowerShell 화면
Windows 관리 PC에서 백엔드 3000 포트가 외부에 공개되지 않았음을 확인한 PowerShell 화면
Windows 관리 PC에서 MySQL 3306 포트가 외부에 공개되지 않았음을 확인한 PowerShell 화면
Windows 관리 PC에서 Redis 6379 포트가 외부에 공개되지 않았음을 확인한 PowerShell 화면

8080TcpTestSucceeded : True이고 나머지는 False여야 합니다. 결과가 다르면 포트를 임의로 닫지 말고 출력 전체를 Codex에 전달합니다.

문제가 발생했을 때 A, B, C로 나누어 해결 요청

문제가 발생하면 09단계로 진행하지 않습니다. 증상에 해당하는 프롬프트 하나를 같은 Codex CLI 대화에 전달합니다. 원인이 둘 이상으로 보이면 A부터 확인하고 B, C 순서로 진행합니다.

A: Compose와 MySQL 문제

컨테이너 실행, MySQL 연결, Health Check 또는 기존 관리자 데이터에 문제가 있을 때 사용합니다.

08단계 전체 Compose 검증에서 A 범위 문제가 발생했어.
내가 전달하는 docker compose ps와 로그를 기준으로 Backend와 MySQL의 Compose 구성, Health Check, 네트워크와 기존 볼륨 연결을 확인해 줘.
현재 Schema에서 실제 관리자 Table과 식별 Column을 확인하고, 데이터 변경 전에는 읽기 전용으로 기존 관리자 계정과 데이터 보존 상태를 검사해 줘.
기존 Volume을 임의로 교체하거나 초기화하지 말고, 삭제·초기화·복원이 필요하면 실행하지 말고 먼저 이유와 안전한 절차를 보고해 줘.
A 원인, 최소 수정 범위와 수정 후 검사 결과를 구분해서 보고하고 암호나 Database URL은 출력하지 마.
Redis와 Nginx는 A 확인에 필요한 범위를 넘어 변경하지 마.

B: Redis 세션 문제

백엔드 재시작 후 로그인이 풀리거나 로그아웃 뒤 세션이 남는 경우에 사용합니다.

08단계 전체 Compose 검증에서 B 범위 문제가 발생했어.
로그인, Backend 재시작 뒤 세션 유지, 로그아웃 뒤 보호 API 차단 결과와 내가 전달하는 Backend·Redis 로그를 확인해 줘.
Redis 세션 저장소 사용 여부, Backend와 Redis의 연결, 쿠키 설정과 세션 저장·삭제 흐름을 검사해 줘.
세션 ID, 세션 비밀값, 쿠키와 Redis 키의 실제 값은 출력하지 마.
B 원인, 최소 수정 범위와 수정 후 세션 유지·로그아웃 검사 결과를 구분해서 보고해 줘.
MySQL 데이터와 Nginx 구성은 B 확인에 필요한 범위를 넘어 변경하지 마.

C: Nginx와 WebUI 연결 문제

8080이 열리지 않거나 화면, API 연결, React 경로 새로고침 또는 공개 포트에 문제가 있을 때 사용합니다.

08단계 전체 Compose 검증에서 C 범위 문제가 발생했어.
내가 전달하는 docker compose ps, Nginx·Backend 로그와 WebUI 오류를 기준으로 원인을 확인해 줘.
Nginx의 React 배포용 빌드 제공, /api/ 전달, React 경로 직접 접근과 새로고침을 검사해 줘.
프런트엔드 API 경로, Nginx proxy_pass 대상, Backend 수신 주소와 Compose 네트워크 연결을 실제 설정으로 대조해 줘.
외부에는 Nginx 8080만 공개되고 Backend, MySQL과 Redis 포트는 공개되지 않는지 확인해 줘.
C 원인, 최소 수정 범위와 수정 후 8080 WebUI·API·공개 포트 검사 결과를 구분해서 보고해 줘.
MySQL 데이터와 Redis 세션 정책은 C 확인에 필요한 범위를 넘어 변경하지 마.

6. 실습 프로세스 중지와 최종 상태 확인

08단계 검증을 마쳤고 바로 다음 실습을 진행하지 않는다면, 검증 결과를 기록하기 전에 서버 자원과 호스트의 8080 포트를 계속 사용하지 않도록 Compose 서비스를 중지합니다.

cd /home/apple2ne1/projects/vibe-coding-platform
docker compose stop
docker compose ps -a
git status --short

docker compose stop은 백엔드, MySQL, Redis와 Nginx 컨테이너의 프로세스만 중지합니다. 컨테이너, Compose 네트워크와 MySQL 볼륨을 삭제하지 않으므로 다음 실습에서 같은 구성으로 다시 시작할 수 있습니다.

docker compose ps -aSTATUS 열에서 네 서비스가 모두 중지된 상태인지 확인합니다. git status --short에서는 구현과 검증에 해당하는 변경만 남아 있어야 합니다. 실행 중인 서비스가 남아 있거나 예상하지 못한 Git 변경이 있으면 반복 실행하거나 파일과 다른 컨테이너를 임의로 변경하지 말고 출력 전체를 같은 Codex CLI 대화에 전달합니다.

다음 실습을 시작할 때는 프로젝트 루트에서 서비스를 다시 실행하고 정상 상태를 확인합니다.

docker compose start
docker compose ps

구성이나 이미지가 변경된 뒤 다시 실행하는 경우에는 해당 단계의 안내에 따라 docker compose up -d --build를 사용합니다.

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

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

08단계 구현과 검증은 앞 절차에서 완료했어. 검증을 다시 실행하지 말고 현재 작업 기록과 실제 결과만 확인해 줘.
webui-gate.md에는 전체 Compose 실행과 8080 WebUI 통합 확인 결과를 기록하고, 문제가 발생하여 A, B 또는 C 진단을 수행했다면 해당 범위의 원인·수정·재검사 결과도 구분해 기록해 줘. local-development.md에는 Compose 실행·중지·재시작·복구 방법을, README.md에는 개발·통합 접속 주소와 공개 포트를 기록해 줘. compose.yaml, Dockerfile, Nginx 설정과 환경변수 예시는 실제 구성이 변경된 경우에만 맞춰 갱신해 줘.
실행하지 않은 검사는 통과로 기록하지 말고 기존 기록을 삭제하거나 덮어쓰지 마.
현재 코드와 확인된 결과를 기준으로 README.md, docs/requirements/admin-platform.md,
docs/development-guides/local-development.md와 docs/development-guides/webui-gate.md의
현재 단계와 구현 완료 상태가 서로 다른지도 확인해 줘.
현재 단계와 구현 완료 상태는 요구사항 변경이 아니라 진행 기록이야. 현재 코드와 확인된 검증 결과로 이번 단계의 완료 조건을 충족한 것이 확인되면, admin-platform.md를 포함한 위 문서의 오래되거나 충돌하는 진행 기록을 사용자에게 다시 묻지 말고 이번 단계 완료로 일치시켜 줘. 요구사항·기준·기존 검증 결과는 바꾸지 마.
실패·미수행·확인 불가 항목 때문에 완료 조건을 충족하지 못했다면 완료로 바꾸지 말고 실제 상태와 다음 해결 방법을 기록해 줘.

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

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

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

스테이징과 커밋은 실행하지 마.
Codex CLI에 08단계의 실제 검증 결과를 관련 문서에 기록하도록 요청한 화면

Codex의 완료 보고에서 변경한 파일, 기록한 실제 검증 결과, 문서 간 단계 상태의 일치 여부, 남은 실패·미수행 항목과 다음 단계 진행 가능 여부를 확인합니다.

Codex CLI가 검증 기록 문서를 갱신하고 08단계 완료 상태의 일치 여부와 09단계 진행 가능 여부를 보고한 화면

8. 최종 변경 검토와 커밋

Codex의 완료 보고와 현재 Git 상태를 기준으로 이번 단계에서 확인한 파일만 사용자가 명시적으로 스테이징합니다. Codex는 앞의 기록 요청에서 스테이징하거나 커밋하지 않습니다.

cd /home/apple2ne1/projects/vibe-coding-platform
git status --short
git diff --name-only
git ls-files --others --exclude-standard

git add -- \
  .dockerignore \
  README.md \
  backend/.env.example \
  backend/package-lock.json \
  backend/package.json \
  backend/src/app.ts \
  backend/src/server.ts \
  backend/src/session-store.ts \
  backend/tsconfig.build.json \
  compose.yaml \
  docker/backend.Dockerfile \
  docker/nginx.Dockerfile \
  docs/development-guides/local-development.md \
  docs/development-guides/webui-gate.md \
  docs/requirements/admin-platform.md \
  frontend/package-lock.json \
  frontend/package.json \
  frontend/vite.config.ts \
  nginx/default.conf

git status --short
git diff --cached --check
git --no-pager diff --cached --name-status
git --no-pager diff --cached
VS Code 원격 터미널에서 최종 변경 전 Git 상태를 확인한 화면
VS Code 원격 터미널에서 08단계의 스테이징 파일 목록과 검사 결과를 확인한 화면

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

git commit -m "feat: complete 08 step"
git status --short
git log -1 --oneline
VS Code 원격 터미널에서 08단계의 19개 변경 파일을 feat complete 08 step 메시지로 커밋한 결과

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

정식 작업 지시서 해설

이 절은 새로운 작업을 지시하거나 추가 명령을 실행하는 단계가 아닙니다. 정식 작업 지시서 실행, 구현 결과 검증, Codex의 검증 기록 갱신과 커밋까지 마친 뒤 읽는 복습용 해설입니다.

비전공자도 앞서 수행한 요구사항이 왜 필요했는지, 어떤 구성 요소가 바뀌었는지, 구현이 어떻게 동작하고 무엇을 검증했는지를 이해할 수 있도록 쉽게 풀어서 설명합니다. 아래의 파일명, 주소와 명령은 완료된 구성을 이해하기 위한 참고 정보이며, 별도의 실행 안내가 없는 한 다시 실행하지 않습니다.

1. 완성된 결과

08단계에서는 07단계까지 개발한 관리자 애플리케이션을 Docker Compose 기반의 1차 통합 환경으로 구성했습니다. 애플리케이션 기능을 새로 추가한 것이 아니라, 개발 환경에서 따로 실행하던 구성 요소를 하나의 진입점으로 연결한 단계입니다. 실제 실행 결과에서는 다음 네 서비스가 각각 컨테이너로 실행되었습니다.

  • backend: Express API를 실행합니다.
  • mysql: 관리자 계정과 애플리케이션 데이터를 저장합니다.
  • redis: 로그인 세션을 저장합니다.
  • nginx: React 배포용 파일을 제공하고 /api/ 요청을 백엔드로 전달합니다.

프런트엔드는 Compose에서 계속 실행되는 별도의 서비스나 컨테이너로 표시되지 않습니다. Nginx 이미지를 만드는 과정에서 React 소스를 배포용 정적 파일로 빌드한 뒤 Nginx가 그 결과를 제공합니다. 따라서 이 단계의 Compose 통합은 backend, mysql, redis, nginx 네 서비스를 실행하고, React 배포용 빌드 결과를 nginx 서비스에 포함하는 구조입니다.

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

vibe-coding-platform/
├── .dockerignore
├── README.md
├── compose.yaml
├── docs/
│   ├── development-guides/
│   │   ├── local-development.md
│   │   └── webui-gate.md
│   ├── requirements/
│   │   └── admin-platform.md
│   └── work-orders/
│       └── 08-production-integration.md
├── backend/
│   ├── .env.example
│   ├── package.json
│   ├── package-lock.json
│   ├── tsconfig.build.json
│   └── src/
│       ├── app.ts
│       ├── server.ts
│       └── session-store.ts
├── frontend/
│   ├── package.json
│   ├── package-lock.json
│   └── vite.config.ts
├── docker/
│   ├── backend.Dockerfile
│   └── nginx.Dockerfile
├── nginx/
│   └── default.conf

위 구조는 08단계 완료 커밋 화면에 표시된 변경 파일 목록을 기준으로 정리한 결과입니다. 프런트엔드 빌드 결과는 docker/nginx.Dockerfile이 만드는 Nginx 이미지 안에 포함되며 frontend라는 상시 실행 서비스를 별도로 만들지 않습니다.

기존 Vite 개발 환경의 5173 구성은 제거하지 않고 유지하되, 이번 사용자의 최종 수동 검증에서는 Vite 개발 서버를 다시 실행하지 않았습니다. 08단계의 실제 화면 검증은 Nginx 단일 진입점인 http://192.168.1.231:8080에서 수행했습니다. 따라서 이번 수동 검증 결과를 51738080 두 환경을 모두 새로 확인한 결과로 기록하지 않습니다.

2. 구현 단계와 사용자 검증 단계를 구분한 이유

정식 작업 지시서의 구현은 A, B, C 순서를 유지합니다.

  1. A에서 백엔드와 MySQL을 Compose로 전환하고 기존 데이터와 볼륨을 보존합니다.
  2. B에서 MemoryStore를 Redis 세션 저장소로 교체합니다.
  3. C에서 React 배포용 빌드와 Nginx 단일 진입점을 추가합니다.

이 순서는 Codex가 변경 범위를 작게 나누고 다음 구성 요소를 추가하기 전에 관련 자동 검사와 회귀 검사를 수행하기 위한 구현 안전장치입니다. A의 데이터 보존과 기존 기능 검사가 통과해야 B로 이동하고, B의 세션 검사가 통과해야 C로 이동합니다. 필수 검사에 실패하거나 실행하지 못한 항목이 있으면 다음 하위 작업으로 넘어가지 않고 실패 또는 미수행으로 구분합니다.

반면 사용자는 구현이 끝난 뒤 A, B, C 환경을 각각 다시 만들지 않습니다. 전체 Compose 환경을 먼저 실행하여 8080에서 한 번에 확인하고, 문제가 발견된 경우에만 A, B 또는 C 진단 프롬프트를 사용합니다. 구현 순서를 나눈 것과 최종 사용 환경을 한 번에 검증하는 것은 서로 다른 목적의 절차입니다.

이 방식은 정상인 구성을 반복해서 실행하는 절차를 줄이면서도 문제가 발생한 영역을 다음과 같이 구분할 수 있게 합니다.

  • A: Compose 실행, MySQL 연결, Health Check, 기존 데이터 또는 볼륨 문제
  • B: Redis 연결, 백엔드 재시작 뒤 세션 유지 또는 로그아웃 문제
  • C: Nginx, /api/ 전달, React 경로 새로고침 또는 공개 포트 문제

3. 전체 Compose 환경의 동작 원리

Windows 관리 PC에서 http://192.168.1.231:8080을 열면 요청은 먼저 Nginx에 도착합니다. Nginx는 요청 종류에 따라 다음과 같이 처리합니다.

Windows 관리 PC
        ↓ http://192.168.1.231:8080
Nginx
  ├── /         → React 배포용 정적 파일
  └── /api/     → Express 백엔드
                         ├── MySQL: 사용자와 애플리케이션 데이터
                         └── Redis: 로그인 세션

백엔드, MySQL과 Redis는 Compose 네트워크 안에서 통신합니다. 이 서비스들은 호스트에 포트를 게시하지 않으며 외부 요청은 Nginx를 통해서만 들어옵니다.

현재 Nginx 포트 매핑은 다음과 같이 확인되었습니다.

호스트 8080 → Nginx 컨테이너 8080

따라서 현재 구성의 게시 포트를 확인할 때에는 컨테이너 포트 8080을 지정합니다.

docker compose port nginx 8080

4. MySQL 데이터 보존과 Health Check의 의미

Compose로 전환할 때 기존 MySQL 볼륨을 임의로 새 볼륨으로 교체하면 관리자 계정과 기존 데이터가 사라질 수 있습니다. 정식 작업 지시서가 변경 전에 현재 MySQL 데이터와 볼륨 상태를 확인하고, 기존 볼륨과 관리자 계정을 보존하도록 한 이유입니다.

기존 볼륨을 그대로 연결할 수 없거나 새 볼륨으로 옮겨야 한다면 먼저 현재 볼륨과 백업을 식별하고, 검증된 방법으로 격리된 복원 확인을 마친 뒤 적용 전후 데이터를 비교해야 합니다. 볼륨 삭제·초기화 또는 실제 데이터 복원이 필요하면 Codex가 임의로 실행하지 않고 이유, 영향과 복구 대상을 보고한 뒤 사용자 승인을 기다려야 합니다.

이번 완료 기록에서는 기존 데이터 볼륨을 보존한 상태로 MySQL 컨테이너가 healthy인 결과가 확인되었습니다. healthy는 컨테이너 프로세스가 실행 중이라는 의미를 넘어, Compose에 정의된 Health Check가 통과했음을 나타냅니다. 다만 이 상태만으로 기존 데이터가 모두 보존되었거나 백업을 실제로 복원할 수 있음이 증명되지는 않습니다. 기존 관리자 계정과 필요한 데이터의 읽기 전용 확인 결과를 함께 보아야 하며, 과거 특정 시점의 데이터와 현재 데이터를 행 단위로 비교한 결과까지 확인한 것으로 확대해 해석하지 않습니다.

5. Redis 세션 검증의 의미

기존 MemoryStore는 세션을 백엔드 프로세스의 메모리에 보관하므로 백엔드를 재시작하면 로그인 상태가 사라질 수 있습니다. Redis 세션 저장소를 사용하면 백엔드 프로세스와 세션 저장 위치가 분리됩니다.

이번 검증에서는 로그인한 상태로 백엔드 컨테이너만 재시작한 뒤 Dashboard와 Text Tool을 다시 열었습니다. 백엔드와 Redis가 정상 상태로 돌아온 뒤에도 로그인 상태가 유지되었으므로 세션이 백엔드 프로세스의 임시 메모리에만 의존하지 않는 동작을 확인했습니다.

로그아웃한 뒤 /admin 또는 /admin/text-tool을 직접 열었을 때 로그인 화면으로 이동한 결과도 확인했습니다. 이는 로그아웃 뒤 기존 세션으로 관리자 화면에 계속 접근할 수 없어야 한다는 보호 동작을 확인한 것입니다.

세션에는 설계한 최소 사용자 식별 정보만 저장해야 하며, 세션 ID, 세션 비밀값, Cookie와 Redis 키의 실제 값은 로그나 완료 보고에 출력하지 않습니다. 화면에서 기능이 동작한다는 사실만으로 저장 데이터의 최소화 여부까지 증명되는 것은 아니므로, 이 항목은 구현 코드와 자동 검사 결과도 함께 확인해야 합니다.

6. Nginx 단일 진입점과 공개 포트 검증

docker compose ps에서 백엔드의 3000/tcp, MySQL의 3306/tcp33060/tcp, Redis의 6379/tcp가 표시되는 것은 컨테이너가 사용하는 포트입니다. 호스트 포트->컨테이너 포트 형식이 없으면 Windows 관리 PC에 게시된 포트가 아닙니다.

실제 검사에서는 다음 결과를 확인했습니다.

검사 포트 대상 결과
8080 Nginx 연결 성공
3000 백엔드 직접 연결 실패
3306 MySQL 직접 연결 실패
6379 Redis 직접 연결 실패

따라서 Windows 관리 PC에서는 Nginx의 8080만 사용할 수 있고 백엔드, MySQL과 Redis에는 직접 접근할 수 없는 1차 통합 경계를 확인했습니다. 이 결과는 해당 포트의 외부 게시 여부를 확인한 것이며, 방화벽·인증·TLS를 포함한 최종 운영 보안 전체를 검증한 결과는 아닙니다.

7. 자동 검사와 WebUI 통합 검증의 역할

자동 검사는 Compose 구문, 프런트엔드 빌드와 타입, 서비스 상태, Health Check, API 응답과 같은 기술 조건을 확인합니다. 검사 명령이 정상 종료하고 정식 작업 지시서의 성공 기준을 충족한 항목만 통과로 기록합니다. 실행하지 못한 검사는 화면이 정상이라는 이유로 통과 처리하지 않습니다.

WebUI 검증은 자동 검사만으로 판단하기 어려운 실제 사용자 흐름을 Windows 관리 PC의 웹 브라우저에서 확인합니다. 화면이 열리지 않거나 기능이 동작하지 않으면 다른 자동 검사 결과만으로 08단계를 완료하지 않고, 실제 증상과 로그를 Codex에 전달하여 해당 A, B 또는 C 범위를 복구한 뒤 다시 확인합니다.

8. WebUI 통합 검증에서 확인한 내용

8080 통합 환경에서 다음 항목을 실제 화면으로 확인했습니다.

  1. 관리자 로그인 화면이 표시되었습니다.
  2. 로그인 뒤 관리자 화면과 Text Tool이 동작했습니다.
  3. /admin/admin/text-tool을 직접 열거나 새로고침해도 React 화면이 유지되었습니다.
  4. 백엔드 재시작 뒤에도 로그인 세션이 유지되었습니다.
  5. 로그아웃 뒤 관리자 전용 주소를 직접 열면 로그인 화면으로 이동했습니다.
  6. Nginx만 8080으로 게시되고 내부 서비스 포트에는 Windows 관리 PC에서 직접 연결할 수 없었습니다.

이 결과는 Compose, Nginx, 백엔드, MySQL과 Redis가 연결된 상태에서 기존 관리자 기능이 동작한다는 1차 통합 기준선입니다. 문제가 없었으므로 별도의 A, B 또는 C 장애 진단 프롬프트는 실행하지 않았습니다.

9. 검증 기록을 별도 프롬프트로 갱신한 이유

구현 프롬프트에는 사용자가 아직 수행하지 않은 WebUI 결과를 미리 기록하는 작업을 넣지 않았습니다. 사용자가 실제 화면과 포트를 확인한 뒤 별도의 Codex에 검증 결과 기록 요청 프롬프트로 다음 문서를 맞췄습니다.

  • docs/development-guides/webui-gate.md: 전체 Compose 실행, 8080 WebUI와 포트 확인 결과
  • docs/development-guides/local-development.md: Compose 실행·중지·재시작과 장애 복구 방법
  • README.md: 개발·통합 접속 주소와 공개 포트
  • docs/requirements/admin-platform.md: 확인된 구현 상태와 현재 단계 기록

실행하지 않은 검사는 통과로 기록하지 않고, 실패·미수행·확인 불가 항목이 있으면 완료 상태로 바꾸지 않는 것이 기록 원칙입니다.

10. 제한과 다음 단계

08단계는 실제 운영 환경의 완성이 아니라 기존 기능과 서비스 연결을 확인한 1차 통합 기준선입니다. Nginx만 외부에 게시한 결과도 이 단계에서 확인한 네트워크 진입점의 범위를 뜻하며, 방화벽·접근 제어·TLS를 포함한 최종 운영 보안 전체가 완성되었다는 의미는 아닙니다. 현재 확인한 주소는 http://이며 실제 도메인, 인증서와 secure 쿠키를 사용하는 HTTPS 검증은 수행하지 않았습니다. 이 항목은 15단계 범위입니다.

Redis 장애 정책, 개발·통합 Compose 환경 분리, 로그인 보안, 자동 검사, 백업·복원과 운영 관측 기능도 이후 단계에서 보강합니다. 09단계에서는 이번 기준선을 보존하면서 현재 구조와 최종 목표 구조의 차이를 분석합니다.

Codex가 갱신한 단계 산출물과 검증 기록

앞의 7. Codex에 검증 결과 기록 요청에서 Codex가 이 단계의 산출물과 검증 기록을 갱신합니다. 아래 내용은 Codex가 기록할 항목에 대한 설명입니다.

이 작업은 여러 문서에 같은 단계의 실행 명령, 검증 결과와 현재 제한을 서로 맞게 기록해야 하므로 비전문가가 직접 수정하면 항목을 빠뜨리거나 서로 다른 내용을 남기기 쉽습니다. Codex는 앞 단계에서 확인한 작업 기록과 실제 결과를 기준으로 관련 문서를 한 번에 비교하여 갱신할 수 있으므로 사용자가 이 파일들을 직접 수정할 필요가 없습니다.

구현 전 08단계 작업 지시서에 Compose 전환 범위와 기존 개발 환경 보존 조건을 확정합니다. 구현 중에는 compose.yaml, .dockerignore, Dockerfile, Nginx 설정, .env.example과 관련 애플리케이션 소스를 같은 작업에서 갱신합니다. 구현 완료 후에는 전체 Compose 실행과 8080 통합 화면 결과를 webui-gate.md의 08단계에 기록합니다. 문제가 발생하여 A, B 또는 C 진단을 실제로 수행한 경우에만 해당 원인, 수정 내용과 재검사 결과를 추가합니다.

Codex에 의해 local-development.md에 기록되는 내용

.dockerignore 생성 시점, Docker와 Docker Compose 전제 조건, .env.example로부터 실제 환경 파일을 준비하는 순서, docker compose up -d --build·상태·로그·중지 명령을 추가합니다. Vite 5173 개발 주소와 Nginx 8080 통합 주소를 분리하고 MySQL·Redis·백엔드의 외부 공개 여부도 기록합니다.

Codex에 의해 README.md에 기록되는 내용

기존 5173 개발 주소와 이번에 확인한 8080 통합 주소를 구분하여 루트 README.md를 갱신합니다. 이번 절차에서 5173을 다시 실행하지 않았다면 이전 기준선 또는 구성 보존으로 기록하고, 이번 단계에서 새로 통과한 WebUI 결과로 기록하지 않습니다.

## 현재 단계

08단계: 운영 구성을 차례로 추가하기

## 완료된 기능

- Docker Compose 기반 MySQL, Redis, 백엔드와 Nginx 통합
- Redis 세션 저장소
- React 배포용 빌드
- Nginx의 `/` 정적 파일 제공과 `/api` 리버스 프록시
- React 경로 직접 접근과 새로고침

## 실행 주소

- 개발 환경: `http://<서버-IP>:5173`
- 1차 통합 환경: `http://<서버-IP>:8080`
- 통합 환경의 외부 공개 포트는 Nginx `8080` 하나입니다.

## 검증

- Compose 구문, Health Check, 데이터 유지와 공개 포트를 확인합니다.
- `8080` 통합 환경에서 관리자 로그인, Dashboard, Text Tool, 백엔드 재시작 뒤 세션 유지와 로그아웃을 확인합니다.
- `5173` 개발 환경을 이번 절차에서 다시 실행하지 않았다면 미수행 또는 이전 기준선으로 구분합니다.

관련 문서

참고 자료 및 출처


Previous article
Next article