# AI DEV 개발·배포 매뉴얼 (개발자용)

행번 계정 하나로 **Coder에서 개발**하고, **Gitea에 push**, **Kubero 또는 Coolify로 배포**합니다.
(배포 도구는 Kubero·Coolify 중 하나를 사용할 수 있습니다. [9번](#9-배포-gitea--kubero--coolify)에 두 방법을 모두 정리해 두었습니다.)
DB(PostgreSQL)·파일저장소(MinIO)·AI(LiteLLM - key 제외)는 워크스페이스에 미리 연결되어 있습니다.

<!-- 이미지: `![설명](images/파일명.png)` 자리에 맞춰 캡처 후 images/ 에 추가 -->

## 목차

**최초 1회**
[1. 로그인](#1-로그인) → [2. 워크스페이스 만들기](#2-워크스페이스-만들기-최초-1회) → [3. VS Code 열기](#3-vs-code-열기) → [5. AI 키 등록](#5-ai-키-등록-최초-1회)

**앱마다**
[6. 새 프로젝트 시작](#6-새-프로젝트-시작) → [7. 개발](#7-개발) → [8. 로컬 실행·확인](#8-로컬-실행확인) → [9. 배포](#9-배포-gitea--kubero--coolify) → [9-5. 포털에 공유](#9-5-포털에-공유)

```
[최초 1회] 로그인(1) → 워크스페이스 생성(2) → VS Code(3) → AI 키 등록(5)
[앱마다]   new-project + git init(6) → 개발·커밋(7) → 로컬 확인(8) → 레포지토리 생성·push·배포(Kubero/Coolify)(9) → 포털 공유(9-5)
```

## 0. 서비스 종류

| 용도 | 주소 |
|---|---|
| AI DEV 포털 (시작점) | https://portal.bokdev.in |
| Coder (개발 워크스페이스) | https://coder.bokdev.in |
| Gitea (코드 저장소, 원격 레포지토리) | https://gitea.bokdev.in |
| Kubero (배포(k8s 기반)) | https://kubero.bokdev.in |
| Coolify (배포(Docker 기반)) | https://coolify.bokdev.in |
| 배포된 앱 (Kubero) | `https://<프로젝트명>.playground.bokdev.in` |
| 배포된 앱 (Coolify) | `https://<프로젝트명>.apps.bokdev.in` |

모든 서비스는 **행번 계정(SSO)** 으로 로그인합니다.

![개발 및 배포 흐름도](images/architecture.svg)

코드에서 쓰는 접속정보(DB·S3)는 `.project-env` 파일로 자동 제공됩니다. 직접 입력할 값이 없습니다.

## 1. 로그인

1. https://portal.bokdev.in 접속
   <!-- TODO: 포털 주소 portal.bokdev.in / backstage.bokdev.in 중 확정 -->
2. 행번 계정으로 로그인
   - 아이디: 본인 행번 (예: `2620227`)  
   - 비밀번호: 본인 비밀번호 (초기 비밀번호: `bok1234!!` + `행번 7자리`)  
   ![포털 로그인 화면](images/01-login.png)

3. 이후 Coder·Gitea·Kubero는 추가 로그인 없이 같은 계정으로 열립니다.
   - Kubero의 경우 `OAuth로 로그인하기`를 눌러 SSO 로그인이 가능합니다.
   ![포털 로그인 화면](images/portal.bokdev.in_.png)

## 2. Coder 워크스페이스 만들기 (최초 1회)

Coder 워크스페이스 = 본인 전용 개발 컨테이너(VS Code + 개발 도구 일체).

> ⚠️**주의**: 같은 브라우저에 다른 계정으로 Gitea 로그인이 남아 있으면 그 계정으로 연동됩니다.  
> 승인 전에 Gitea에서 로그아웃했는지 확인하거나, **시크릿 모드**에서 진행합니다.   
> Coder와 Gitea의 로그인 계정이 일치하지 않는 경우 Workspace 생성 후 계정 불일치로 Push가 되지 않을 수 있습니다.

1. https://coder.bokdev.in → **Workspaces** → **Create Workspace** (템플릿: `aidev`)
2. 설정값 입력
   - **Name**: 워크스페이스 이름 (예: `ws-aidev-<행번>`)
   - **External Authentication**: Gitea — **애플리케이션 승인** 클릭
   - **CPU / Memory / Disk**: 기본값(2 Core / 4 GiB / 10 GiB) 사용. 추후 변경 가능.

   ![Gitea 계정 연동](images/02-coder-gitea.png)

3. **Create Workspace** 클릭
4. 최초 빌드는 2~5분 소요. 상태가 **Running**이 되면 완료.

> **주의**: 워크스페이스는 한 번 만들면 계속 사용합니다.

## 3. VS Code 열기

1. 워크스페이스 화면에서 **VS Code Web** 아이콘 클릭
2. `/home/coder/projects` 폴더가 자동으로 열립니다. ("Yes, I trust the authors" 클릭)
3. 안에 **`sample`** 폴더가 있습니다. DB·S3 연결이 확인된 참조용 예제이며 **직접 수정하지 않습니다**. [6번](#6-새-프로젝트-시작)에서 복사해 사용합니다.
   <!-- TODO: 예제 폴더명 sample → connection-validation 변경 반영 여부 확인 (new-project 스크립트 포함) -->

> 작업 파일은 반드시 `/home/coder/projects` 아래에 둡니다. 이 폴더만 워크스페이스 재시작 후에도 보존됩니다.

## 4. 기본 제공 환경

새 워크스페이스에 아래가 설치·연결되어 있습니다.

| 항목 | 내용 |
|---|---|
| 개발 도구 | Java(JDK)/Maven, Node 22, Python 3.12, git, psql |
| 컨테이너 | podman (`docker` 명령도 동일 동작) |
| DB | 본인 전용 PostgreSQL 스키마 (`$DATABASE_URL`) |
| VS Code 확장 | Claude Code, Codex |
| AI CLI | `claude`, `codex` — [5번](#5-ai-키-등록-최초-1회)에서 키 등록 필요 |

## 5. AI 키 등록 (최초 1회)

Claude Code / Codex 는 사내 AI 게이트웨이(LiteLLM)를 사용합니다. 발급받은 본인 virtual key를 한 번만 등록하면 CLI·확장이 모두 공유합니다.

1. 터미널 열기: VS Code 메뉴(좌상단 ☰) → **Terminal → New Terminal**
2. 아래 명령 실행 후 본인 키(`sk-...`) 입력:
   ```bash
   update-litellm-key
   ```
   ```
   LiteLLM virtual key 입력 (sk-...): sk-본인-키
   키 갱신 완료 (len=25). 현재 터미널에 즉시 적용됨.
   ```

> ⚠️ 키 등록·변경 후 '키 갱신 완료' 커맨드가 뜨지 않고 **VS Code(웹)가 응답하지 않을 수 있습니다.**  
> 이는 환경변수 등록을 위해 Coder 환경이 재기동되어야하기 때문이며,  
> Coder Web 창을 아예 끄고 워크스페이스 화면에서 VS Code 서버를 **Stop → Start** 하여 재시작해줍니다. update는 정상적으로 완료되었기 때문에 update-litellm-key를 재입력할 필요 없이 3번을 확인해주세요ㅣ

   ![Coder 완전히 stop 후 start](images/coder-stop-and-start.png)

3. 확인:
   ```bash
   echo $ANTHROPIC_BASE_URL   # https://litellm.bok.or.kr 이면 정상
   claude
   ```
   ```bash
   echo $OPENAI_BASE_URL      # https://litellm.bok.or.kr/v1 이면 정상
   codex
   ```



키는 워크스페이스의 `~/.env`에만 저장됩니다. 키를 바꿀 때도 같은 명령을 다시 실행합니다.

기본 모델은 게이트웨이에 맞춰 설정되어 있습니다.

| 도구 | 기본 모델 | 설정 파일 |
|---|---|---|
| Claude Code | `claude-opus-4-8` | `~/.claude/settings.json` |
| Codex | `gpt-5.5` | `~/.codex/config.toml` |

## 6. 새 프로젝트 시작

`sample` 예제를 복사해 시작합니다.

**(1) 터미널에서 프로젝트 생성** — 반드시 `~/projects` 에서 실행:
```bash
cd ~/projects
cd sample && git pull && cd ..   # 예제 최신화
new-project myapp                # 예제를 ~/projects/myapp 으로 복사 + .project-env 자동 생성
```
`myapp`은 예시입니다. 이 이름(프로젝트 명)은 Gitea 레포지토리명과 동일하게 맞추시면 되고, 영어·숫자·하이픈만 사용합니다.

**(2) git 초기화** — 개발 시작 시점에 합니다. 커밋 이력을 처음부터 관리하기 위함이며, 원격(Gitea) 연결은 배포 단계([9번](#9-배포-gitea--kubero--coolify))에서 합니다:
```bash
cd ~/projects/myapp
git init -b main
git add .
git commit -m "init project"
```

**(3) VS Code로 폴더 열기**: **File → Open Folder…** → `/home/coder/projects/myapp` → OK
왼쪽에 `myapp` 파일 목록이 보이면 완료. 새 터미널은 이 폴더에서 시작됩니다.

**(4) 라이브러리 설치**:
```bash
npm install
```

> **`.project-env`** 는 이 프로젝트의 설정 파일(DB·S3 접속정보)입니다. 폴더에 들어가면(cd) 자동으로 환경변수에 로드됩니다.
> <!-- TODO: 예제 .gitignore 에서 .project-env 제외할지 확인 필요 -->
> LiteLLM 키만 예외로 워크스페이스 공용 `~/.env`([5번](#5-ai-키-등록-최초-1회))에서 관리합니다.

## 7. 개발

- **편집**: 왼쪽 파일 목록에서 파일 선택 → 수정 → Ctrl+S 저장
- **AI 도구**: 프로젝트 폴더 안 터미널에서 `claude` 또는 `codex` 실행. 폴더 밖에서 실행하면 프로젝트 파일을 읽지 못합니다.
- **커밋**: 기능 단위로 수시로 커밋합니다. push는 배포 단계에서.
  ```bash
  git add . && git commit -m "메시지"
  ```
- **DB 접속**:
  ```bash
  psql "$DATABASE_URL"        # 프로젝트 폴더에서 실행 (.project-env 로드 필요)
  ```
- 코드에서는 `process.env.DATABASE_URL`, `process.env.S3_*` 를 사용합니다.
- **`CLAUDE.md`**: 프로젝트 규칙·주의사항을 적어두면 Claude Code가 자동으로 읽고 따릅니다. 예제에 기본 파일이 포함되어 있습니다.
- AI에게는 구체적으로 지시합니다. 예: "로그인 API 만들어줘" 대신 "`src/`에 POST /login 추가, 검증 실패 시 401 반환". 생성된 코드는 [8번](#8-로컬-실행확인)으로 직접 확인 후 커밋합니다.

### 7-1. bkit 플러그인 (선택)

Claude Code에 계획→설계→구현→검증 절차를 더하는 플러그인. 터미널의 `claude` CLI에서만 동작합니다(VS Code 확장 미지원).

설치(최초 1회, `claude` 실행 후 프롬프트에 입력):
```
/plugin marketplace add popup-studio-ai/bkit-claude-code
/plugin install bkit
```

사용: `/pdca pm <기능이름>` — 기능 하나를 계획부터 검증까지 진행. 세분화 명령은 `/pdca plan` `/pdca design` `/pdca do` `/pdca analyze`.

## 8. 로컬 실행·확인

**(1) 실행**
```bash
cd ~/projects/myapp
npm run dev             # 저장 시 자동 재시작
```
`listening on :3000` 이 에러 없이 출력되면 기동 성공.
실패 시 순서대로 확인: ① `npm install` 했는지 ② 코드 문법 오류 ③ 프로젝트 폴더 밖에서 실행(`.project-env` 미로딩).

**(2) 연결 점검** — 앱을 띄우지 않고 DB·S3 연결만 확인:
```bash
npm run db:check        # "DB OK: ..." 이면 정상
npm run minio:check     # "S3 OK: ..." 이면 정상
```
FAIL이면 `.project-env` 값을 확인합니다. 여기서 통과하면 배포 환경에서도 동일하게 동작합니다.

**(3) 브라우저 미리보기** — 워크스페이스는 클러스터 내부라 `localhost:3000`이 PC 브라우저에서 열리지 않습니다. 포트 포워딩을 사용합니다:
1. VS Code 하단 **PORTS** 탭 → **Forward a Port** → `3000` 입력
2. 포워딩된 포트의 **Open in Browser** 클릭 → `https://<자동생성>.coder.bokdev.in`

![브라우저 미리보기](images/coder-ports-preview.png)

**(4) 엔드포인트 확인**
```bash
curl 127.0.0.1:3000/healthz   # {"ok":true}            앱 기동
curl 127.0.0.1:3000/db        # {"ok":true,"now":...}   DB 연결
curl 127.0.0.1:3000/s3        # {"ok":true,"bucket":...} S3 연결
```
`"ok": false` 이면 함께 출력되는 `error` 메시지가 원인입니다.

미리보기 URL은 본인 전용이며 워크스페이스를 끄면 사라집니다. 정식 배포는 [9번](#9-배포-gitea--kubero--coolify).

## 9. 배포 (Gitea → Kubero / Coolify)

배포 단위: Gitea `playground` 조직의 레포지토리 1개 = 배포 앱 1개.
배포 주소는 사용하는 도구에 따라 다릅니다 — **Kubero → `https://<프로젝트명>.playground.bokdev.in`**, **Coolify → `https://<프로젝트명>.apps.bokdev.in`**.

배포 도구는 **Kubero**와 **Coolify** 중 하나를 사용합니다.  
**Gitea 레포지토리 생성([9-1](#9-1-gitea-원격-레포지토리-생성-앱당-1회))과 push([9-2](#9-2-push))는 두 도구 공통**이며, 이후 사용하는 도구에 따라 [9-3A(Kubero)](#9-3a-kubero에-앱-추가-앱당-1회) 또는 [9-3B(Coolify)](#9-3b-coolify에-앱-추가-앱당-1회)를 따릅니다.

| 항목 | Kubero | Coolify |
|---|---|---|
| 배포 주소 | `https://<프로젝트명>.playground.bokdev.in` | `https://<프로젝트명>.apps.bokdev.in` |
| 배포 위치 | `playground` 파이프라인에 앱 추가 | `aidev` 팀 → `aidev` 프로젝트에 앱 추가 |
| 코드 수정 반영 | push 후 **수동 재빌드** (자동 빌드 미연동) | push 시 **자동 재빌드·배포** (webhook 설정 시, [9-3B](#9-3b-coolify에-앱-추가-앱당-1회)) |
| 환경변수 입력 | `.project-env` 업로드 → 자동 파싱 | `.project-env` 값을 붙여넣기 (Developer view) |
| 빌드 방식 | Dockerfile | Dockerfile |

### 9-1. Gitea 원격 레포지토리 생성 (앱당 1회)

1. https://gitea.bokdev.in/playground → 우측 상단 **`+` → New Repository**  
   ![새 저장소 만들기](images/make-new-repository.png)
2. **Owner: `playground`** 로 변경, Repository Name 입력 (예: `myapp`), public 설정
   ![새 저장소 옵션 설정](images/make-new-repository-2.png)
3. README / .gitignore / License 는 체크하지 않음(빈 저장소여야 함) → **Create Repository**

### 9-2. push

```bash
cd ~/projects/myapp
git remote add origin https://gitea.bokdev.in/playground/myapp.git
git push -u origin main
```
- 최초 push 시 Gitea 승인 화면이 뜨면 **Authorize** 클릭([2번](#2-워크스페이스-만들기-최초-1회)에서 승인했다면 생략됨).
- 이후 수정 반영: `git add . && git commit -m "..." && git push`

### 9-3A. Kubero에 앱 추가 (앱당 1회)

▸ 아래 삼각형을 눌러 펼치세요.

<details>
<summary><b>Kubero로 배포</b> — 도메인 <code>*.playground.bokdev.in</code> · 클릭해서 펼치기</summary>

`playground` pipeline을 사용하시면 되며, 사용자는 그 안에 본인 앱만 추가합니다.

1. https://kubero.bokdev.in 접속
2. **`playground`** 파이프라인 선택
3. **Production** 아래의 `+` 버튼을 클릭해 앱을 추가
4. App Name과 환경 ENVIRONMENT VARIABLES 추가
   ![Kubero 배포 앱 추가](images/kubero-pipeline.png)  
   - `.project-env` 파일을 업로드 하면 자동으로 파싱되어 등록됩니다.

> 자동 빌드는 현재 미연동입니다. **코드 수정 후에는 push 하고 Kubero에서 해당 앱의 빌드를 다시 실행합니다.**

</details>

### 9-3B. Coolify에 앱 추가 (앱당 1회)

▸ 아래 삼각형을 눌러 펼치세요.

<details>
<summary><b>Coolify로 배포</b> — 도메인 <code>*.apps.bokdev.in</code> · 클릭해서 펼치기</summary>

Coolify는 "**push → Dockerfile로 자동 빌드·배포**" 방식입니다. 사용자는 `aidev` 프로젝트에 본인 앱만 추가합니다.

1. https://coolify.bokdev.in 접속 → 우측 상단에서 **`aidev`** 팀 선택
2. 좌측 **`Projects` → `aidev`** (서버·DB가 연결된 프로젝트) → **`+ Add Resource`**  
   ![Coolify 팀 선택](images/coolify-team.png)
3. 리소스 종류에서 **`Public Repository`** 선택
   ![Coolify 리소스 종류 선택](images/coolify-new-resource.png)
4. **Repository URL**에 **전체 주소**를 입력 후 **`Check Repository`**:
   `https://gitea.bokdev.in/playground/myapp.git`
   (`playground/myapp` 처럼 줄여 쓰면 실패합니다.)
   > **비공개(private) 레포지토리일 때** — `Public Repository` 로도 받을 수 있습니다. URL에 Gitea 토큰을 끼워 넣습니다:
   > `https://<토큰>@gitea.bokdev.in/playground/myapp.git`
   > - 토큰 발급: Gitea → 우측 상단 프로필 → **Settings → Applications → Generate New Token**. 이름 지정 후 **`repository` 읽기 권한(Read)** 만 체크 → 생성. 표시되는 토큰은 **이때 한 번만** 보이므로 복사해 둡니다.
   > - 발급한 토큰을 위 URL의 `<토큰>` 자리에 넣고 **`Check Repository`**. (토큰이 URL·Coolify 설정에 저장되므로 읽기 전용 권한만 부여합니다.)
5. **Build Pack: `Dockerfile`**, Branch `main`, Port `3000`.
   ![Coolify 저장소 연결](images/coolify-repo.png)
6. **Configuration → Domains** 에서 **`Generate Domain`** 클릭 → `https://<프로젝트명>.apps.bokdev.in` 형태로 지정.
7. **Environment Variables** 에 `.project-env` 값 등록:
   - **Developer view** 에서 `.project-env` 내용을 그대로 붙여넣으면 일괄 등록됩니다. (`cat ~/projects/myapp/.project-env`)
   - `DATABASE_URL` 은 `%20`·`%3D` 인코딩까지 **그대로** 넣습니다(빼면 DB 연결이 깨집니다).
8. **Deploy** 클릭 → **Deployments** 탭에서 빌드 로그 확인. `New container started` / `Deployment finished` 가 보이면 성공.

#### 자동 배포(auto deploy) 설정 (앱당 1회)

Public Repository 방식은 webhook을 걸어야 push가 자동 배포로 이어집니다(Coolify가 push를 스스로 감지하지 못함). Coolify에 별도의 "Auto Deploy" 켜기 단계는 없고, **Webhooks 탭의 URL·Secret을 Gitea에 등록하는 것이 곧 자동 배포 설정**입니다. 앱마다 한 번만 하면 됩니다.

1. **Coolify 앱** → **Configuration → Webhooks** 탭에서, **Gitea** 항목의 **Webhook URL** 과 **Secret** 을 복사합니다. (Secret 칸이 비어 있으면 값을 입력/생성 후 저장)
   ![Coolify Webhook URL·Secret](images/coolify-webhook.png)
2. **Gitea 레포지토리** → `https://gitea.bokdev.in/playground/myapp` → **Settings → Webhooks → Add Webhook → Gitea** 에 등록:
   - **Target URL**: 1번의 Webhook URL
   - **Secret**: 1번의 Secret
   - **Content Type**: `application/json`
   - **Trigger**: Push events, **Active** 체크 → **Add Webhook**
   ![Gitea Webhook 등록](images/gitea-webhook.png)
3. Gitea webhook 화면의 **Test Delivery** 를 누르거나 실제로 `git push` → Coolify **Deployments** 에 새 빌드가 자동으로 뜨면 완료.

> push가 배포를 트리거할지는 앱 **Advanced** 탭의 **`Auto Deploy`** 옵션이 결정하며, **기본값이 켜짐**이라 따로 켤 필요는 없습니다(자동 배포를 끄고 싶을 때만 여기서 해제).
> 설정 후에는 코드를 고쳐 **`git push` 하면 자동으로 다시 빌드·배포**됩니다(Deployments에서 새 빌드 로그 확인). webhook을 걸지 않았다면 앱 화면에서 **Deploy** 를 눌러 수동 배포합니다.

</details>

### 9-4. 확인

배포·재시작 직후 약 1~2분은 초기화(코드 다운로드·설치) 시간입니다. 일시적으로 404가 나오는 경우, 잠시 기다린 후 Ctrl + Shift + R로 강력 새로고침 후 확인해주세요.

```bash
# Kubero로 배포한 경우 (도메인 .playground.bokdev.in)
curl https://<프로젝트명>.playground.bokdev.in/healthz   # {"ok":true}
curl https://<프로젝트명>.playground.bokdev.in/db
curl https://<프로젝트명>.playground.bokdev.in/s3

# Coolify로 배포한 경우 (도메인 .apps.bokdev.in)
curl https://<프로젝트명>.apps.bokdev.in/healthz
curl https://<프로젝트명>.apps.bokdev.in/db
curl https://<프로젝트명>.apps.bokdev.in/s3
```

![배포된 앱](images/08-deployed-app.png)

문제가 있으면 Kubero에서 해당 앱의 빌드/배포 로그를 확인합니다. 로그에 `listening on :3000` 이 보이면 기동 성공입니다.

### 9-5. 포털에 공유

1. [AI DEV Portal](https://portal.bokdev.in)에 접속합니다.
2. `새 프로젝트` 버튼을 눌러 내용을 작성합니다.  
   ![새 프로젝트 버튼](images/portal-add-new-project.png)
3. 프로젝트 제목, Git 저장소 URL(Gitea URL), 간단한 설명, 배포 URL(실제로 서비스에 접속 가능한 URL) 등을 입력합니다.  
   ![새 프로젝트 등록 디테일](images/portal-add-new-project-details.png)
4. 공개 범위를 `공개`로 하는 경우 모두에게 프로젝트가 공유되며, `비공개`로 설정하는 경우 나만 볼 수 있습니다.  
   - 등록 이후에도 공개 범위를 포함하여 프로젝트 내용은 얼마든지 수정 가능하니 편하게 설정해주세요.

## FAQ

- Coder Workspace 켜고 끄기
  - Coder workspace 재기동이 필요한 경우: Coder 워크스페이스 화면에서 **Stop**
  - 다시 사용: **Start** (VS Code Web 아이콘이 뜰 때까지 대기)
  - `~/projects` 만 보존됩니다. 그 외 경로의 파일은 사라질 수 있습니다.

- **로그인을 서비스마다 해야 하나요** → 아니요. 행번 계정 SSO 하나로 전부 로그인됩니다.
- **Coder에서 파일이 사라졌어요** → `~/projects` 밖에 저장한 경우 파일이 유실될 수 있습니다([3번](#3-vs-code-열기)).
- **AI 도구 401 오류** → `update-litellm-key` 재실행([5번](#5-ai-키-등록-최초-1회)). 키가 `sk-`로 시작하는지 확인.
- **AI 도구 400 (Invalid model)** → [5번](#5-ai-키-등록-최초-1회) 표의 기본 모델명 사용.
- **키 등록 후 VS Code가 먹통** → VS Code 서버 Stop → Start([5번](#5-ai-키-등록-최초-1회)).
- **`$DATABASE_URL` 이 비어 있음** → 프로젝트 폴더 안에서 실행해야 `.project-env` 가 로드됩니다([6번](#6-새-프로젝트-시작)).
- **`npm run dev` 가 `Cannot find package ...`** → `npm install` 미실행([6번](#6-새-프로젝트-시작)).
- **push 인증을 물어봄** → Gitea 승인을 아직 안 한 경우. 승인 화면에서 Authorize([9-2](#9-2-push)).
- **다른 계정으로 push/연동됨** → 브라우저에 남아 있던 Gitea 로그인 세션 때문입니다. Gitea 로그아웃 후 재승인하거나 시크릿 창 사용([2번](#2-워크스페이스-만들기-최초-1회)).
- **배포 주소가 404** → ① 배포 직후 1~2분 대기 ② `/healthz` 확인 ③ Kubero/Coolify 배포 로그 확인([9-4](#9-4-확인)).
- **`/healthz` 는 되는데 `/db`·`/s3` 가 500** → 먼저 로컬에서 `npm run db:check` / `minio:check` 통과 확인([8번](#8-로컬-실행확인)). 로컬에서 되면 배포 로그의 에러 메시지 확인. Coolify는 `.project-env` 값(특히 `DATABASE_URL` 의 `%20`/`%3D`)이 그대로인지도 확인.
- **배포가 옛날 코드** → push 됐는지 먼저 확인. Kubero는 빌드 재실행([9-3A](#9-3a-kubero에-앱-추가-앱당-1회)), Coolify는 push 시 자동 재배포([9-3B](#9-3b-coolify에-앱-추가-앱당-1회)).
- **DB가 비어 있음** → 정상입니다. 빈 전용 스키마가 제공되며 테이블은 직접 생성합니다.
- **K8s에 직접 접근하고 싶어요** → 직원은 K8s에 직접 접근하지 않습니다. Coder·Gitea·Kubero로 개발·배포가 완결됩니다.

- git 레포지토리 URL vs 배포 URL

   배포 과정에서 나오는 두 주소는 이름이 비슷해 헷갈리기 쉽지만, **서로 완전히 다른 것**입니다.

   | 종류 | 형태 (예시) | 무엇인가 · 브라우저로 열면 | 매뉴얼에서 쓰는 곳 |
   |---|---|---|---|
   | **git 레포지토리 URL** | `https://gitea.bokdev.in/playground/<프로젝트명>.git` | **내 소스코드가 저장되는 위치**. `git push` 로 코드를 이 주소에 올립니다. (`.git` 을 뗀 주소를 브라우저로 열면 **코드 파일 목록**이 보이며, 실행 중인 앱이 아닙니다.) | [9-2 push](#9-2-push), Coolify의 *Repository URL* 입력([9-3B](#9-3b-coolify에-앱-추가-앱당-1회)) |
   | **배포 URL** | Kubero `https://<프로젝트명>.playground.bokdev.in`<br>Coolify `https://<프로젝트명>.apps.bokdev.in` | **정식 배포되어 항상 켜져 있는 앱 주소**. 브라우저로 열면 **실제로 실행 중인 앱**이 응답합니다. | [9-4 확인](#9-4-확인) |

   > **핵심 구분** — **git 레포지토리 URL = 코드(소스)가 저장된 곳**, **배포 URL = 그 코드가 실제로 실행되어 접속 가능한 앱**.
   > 절차는 항상 이 순서입니다: **코드를 git 레포지토리 URL로 push → 배포 도구(Kubero/Coolify)가 그 코드로 앱을 빌드 → 배포 URL에 앱이 뜸.**
   > 즉 push 하는 주소(git 레포지토리)와 접속해서 보는 주소(배포 URL)는 다릅니다.


## 문의
- IT 전략국 클라우드팀 김창록 팀장
- IT 전략국 정보시스템개발팀 박성록 과장
- IT 전략국 클라우드팀 이혜민 조사역