# SAM 로컬 개발 환경 셋팅 가이드

> 최종 업데이트: 2026-03-09

---

## 1. 사전 준비

### 필수 소프트웨어

| 소프트웨어 | 버전 | 용도 | 설치                                                            |
|-----------|------|------|---------------------------------------------------------------|
| **Docker Desktop** | 최신 | 컨테이너 실행 | [docker.com](https://www.docker.com/products/docker-desktop/) |
| **Git** | 최신 | 버전 관리 | `brew install git`                                            |
| **mkcert** | 최신 | 로컬 SSL 인증서 | `brew install mkcert`                                         |
| **텍스트 에디터** | - | 코드 편집 | JetBrains IDE 권장                                              |

> Docker Desktop이 설치되면 PHP, Node.js, MySQL 등은 **별도 설치 불필요** (Docker 컨테이너 내부에서 실행)

### Git 서버 정보

| 항목 | 값 |
|------|-----|
| Git 서버 | Gitea (자체 호스팅) |
| 서버 주소 | `http://114.203.209.83:3000` |
| 조직 | `SamProject` |

> Gitea 계정이 필요합니다. 팀장에게 계정 생성을 요청하세요.

---

## 2. 저장소 클론

### 디렉토리 구조

```
Works/@KD_SAM/SAM/          ← 루트 (원하는 경로에 생성)
├── api/                     ← Laravel REST API
├── mng/                     ← Laravel 관리자 패널
├── react/                   ← Next.js 프론트엔드
├── docs/                    ← 기술 문서
├── hotfix/                  ← 테스트/QA 문서
├── docker/                  ← Docker 설정 (api 저장소에 포함되지 않음)
├── design/                  ← 디자인 시스템 (선택)
└── planning/                ← 기획 문서 (선택)
```

### 클론 명령어

```bash
# 작업 디렉토리 생성
mkdir -p ~/Works/@KD_SAM/SAM
cd ~/Works/@KD_SAM/SAM

# 5개 저장소 클론
git clone http://114.203.209.83:3000/SamProject/sam-api.git api
git clone http://114.203.209.83:3000/SamProject/sam-manage.git mng
git clone http://114.203.209.83:3000/SamProject/sam-react-prod.git react
git clone http://114.203.209.83:3000/SamProject/sam-docs.git docs
git clone http://114.203.209.83:3000/SamProject/sam-hotfix.git hotfix
```

### 기본 브랜치 전환

```bash
# api, react는 develop 브랜치에서 작업
cd api && git checkout develop && cd ..
cd react && git checkout develop && cd ..
```

> **중요**: `main` 브랜치에서 직접 작업하지 않습니다. 항상 `develop`에서 작업합니다.

---

## 3. Docker 환경 구성

### 3-1. Docker 설정 파일 확인

`docker/` 디렉토리에 모든 Docker 설정이 포함되어 있습니다.

```
docker/
├── docker-compose.yml       ← 메인 Compose 파일
├── .env                     ← Compose 환경변수
├── api/
│   ├── Dockerfile           ← PHP 8.4 + Nginx
│   ├── nginx.conf
│   ├── supervisord.conf
│   └── uploads.ini
├── mng/
│   ├── Dockerfile           ← PHP 8.4 + Nginx
│   ├── nginx.conf
│   ├── supervisord.conf
│   └── uploads.ini
├── react/
│   └── Dockerfile           ← Node 20 + Chromium (PDF용)
├── mysql/
│   ├── init.sql             ← 초기 DB/유저 생성
│   └── my.cnf               ← MySQL 설정
├── nginx/
│   ├── nginx.conf           ← 리버스 프록시 설정
│   └── ssl/                 ← SSL 인증서
└── 5130/
    └── Dockerfile           ← 레거시 PHP 7.3
```

### 3-2. 서비스 구성

| 서비스 | 이미지 | 내부 포트 | 역할 |
|--------|--------|-----------|------|
| **nginx** | nginx:latest | 80, 443 | 리버스 프록시, SSL 종료 |
| **api** | PHP 8.4-fpm | 9000 | REST API 백엔드 |
| **mng** | PHP 8.4-fpm | 9000 | 관리자 패널 |
| **react** | node:20-alpine | 3000 | Next.js 프론트엔드 |
| **mysql** | mysql:8.0 | 3306 | 데이터베이스 |
| **php73** | PHP 7.3-fpm | 9000 | 레거시 5130 앱 |

### 3-3. 네트워크

모든 컨테이너는 `samnet` 브릿지 네트워크로 연결됩니다.

```
[브라우저] → [nginx:443] → api / mng / react (내부 라우팅)
                          → mysql (컨테이너명: sam-mysql-1)
```

---

## 4. hosts 파일 설정

로컬 도메인을 사용하기 위해 hosts 파일을 수정합니다.

```bash
sudo nano /etc/hosts
```

아래 내용을 추가:

```
127.0.0.1 api.sam.kr mng.sam.kr admin.sam.kr dev.sam.kr design.sam.kr plan.sam.kr 5130.sam.kr
127.0.0.1 sam.kr www.sam.kr sales.sam.kr demo.sam.kr
```

---

## 5. SSL 인증서 설정

로컬 HTTPS를 위한 자체 서명 인증서를 생성합니다.

### 5-1. mkcert 설치 및 초기화

```bash
# mkcert 설치 (처음 한 번)
brew install mkcert
mkcert -install    # 로컬 CA를 시스템에 등록
```

### 5-2. 와일드카드 인증서 생성

```bash
cd docker/nginx/ssl/

# *.sam.kr 와일드카드 인증서 생성
mkcert "*.sam.kr" localhost 127.0.0.1 ::1

# 파일명 변경 (nginx.conf에서 참조하는 이름으로)
mv _wildcard.sam.kr+3.pem sam.kr.crt
mv _wildcard.sam.kr+3-key.pem sam.kr.key
```

### 5-3. 포트 포워딩 설정 (macOS)

Docker가 443 포트를 4443으로 매핑하므로, 브라우저에서 표준 443 포트로 접속하려면 포트 포워딩이 필요합니다.

```bash
# pfctl 규칙 적용 (443 → 4443 포워딩)
sudo pfctl -ef docker/nginx/ssl/pf-sam.conf
```

> **참고**: macOS 재부팅 시 이 설정은 초기화되므로 재부팅 후 다시 실행해야 합니다.

**포트 포워딩 없이 사용하려면** `https://api.sam.kr:4443` 처럼 포트 번호를 직접 지정합니다.

---

## 6. 환경변수 (.env) 설정

### 6-1. API (.env)

```bash
cd api
cp .env.example .env
```

`.env` 파일에서 확인/수정할 항목:

```env
# 앱 키 생성은 Docker 실행 후 컨테이너 내에서 수행
# docker exec sam-api-1 php artisan key:generate

APP_NAME="SAM API"
APP_ENV=local
APP_DEBUG=true
APP_URL=https://api.sam.kr/

# DB (Docker 환경에서는 docker-compose가 오버라이드)
DB_HOST=127.0.0.1          # 로컬 직접 접속 시
# DB_HOST=sam-mysql-1      # Docker 환경 (자동 오버라이드)
DB_DATABASE=samdb
DB_USERNAME=samuser
DB_PASSWORD=sampass

# Swagger
L5_SWAGGER_GENERATE_ALWAYS=true
L5_SWAGGER_CONST_HOST=https://api.sam.kr/

# Sanctum 토큰 (분 단위)
SANCTUM_ACCESS_TOKEN_EXPIRATION=120
SANCTUM_REFRESH_TOKEN_EXPIRATION=10080

# 내부 통신 키 (MNG ↔ API)
INTERNAL_EXCHANGE_SECRET=     # 팀 내부 문서에서 확인
```

> **API 키, Firebase, AI 서비스 키** 등 민감한 값은 팀 내부 문서(노션)에서 별도 공유합니다.

### 6-2. MNG (.env)

```bash
cd mng
cp .env.example .env
```

```env
APP_NAME=SAM-MNG
APP_ENV=local
APP_DEBUG=true
APP_URL=https://mng.sam.kr

DB_HOST=sam-mysql-1
DB_DATABASE=samdb
DB_USERNAME=samuser
DB_PASSWORD=sampass

# API 서버 연동
API_BASE_URL=https://api.sam.kr

# 내부 통신 키 (API와 동일한 값)
INTERNAL_EXCHANGE_SECRET=     # API의 값과 동일하게 설정
```

### 6-3. React (.env.local)

```bash
cd react
cp .env.example .env.local
```

```env
NEXT_PUBLIC_APP_ENV=local
NEXT_PUBLIC_API_URL=https://api.sam.kr
NEXT_PUBLIC_FRONTEND_URL=https://dev.sam.kr
NEXT_PUBLIC_AUTH_MODE=sanctum

# API Key (서버 사이드 전용 - NEXT_PUBLIC_ 접두사 붙이지 말 것!)
API_KEY=                      # 팀 내부 문서에서 확인

# 개발 도구
NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=false

# Puppeteer (PDF 생성 - Docker에서는 자동 설정)
PUPPETEER_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
```

### 6-4. docs / hotfix

별도 환경변수 설정 불필요 (순수 마크다운 문서 저장소)

---

## 7. Docker 실행

### 7-1. 최초 실행

```bash
cd docker

# 이미지 빌드 + 컨테이너 시작
docker compose up -d --build
```

> 첫 실행 시 이미지 빌드에 5~10분 소요될 수 있습니다.

### 7-2. 초기 설정 (최초 1회)

```bash
# API: 앱 키 생성 + 의존성 설치 + 마이그레이션
docker exec sam-api-1 php artisan key:generate
docker exec sam-api-1 composer install
docker exec sam-api-1 php artisan migrate
docker exec sam-api-1 php artisan l5-swagger:generate

# MNG: 앱 키 생성 + 의존성 설치
docker exec sam-mng-1 php artisan key:generate
docker exec sam-mng-1 composer install
docker exec sam-mng-1 npm install
docker exec sam-mng-1 npm run build
```

> React는 Docker 컨테이너 시작 시 자동으로 `npm install` + `npm run dev`가 실행됩니다.

### 7-3. 일상적인 시작/종료

```bash
cd docker

# 시작
docker compose up -d

# 종료
docker compose down

# 로그 확인
docker compose logs -f            # 전체
docker compose logs -f api        # API만
docker compose logs -f react      # React만

# 컨테이너 상태 확인
docker compose ps
```

---

## 8. 접속 확인

### 8-1. 로컬 도메인

| 서비스 | URL | 설명 |
|--------|-----|------|
| **프론트엔드** | `https://dev.sam.kr` | Next.js 사용자 화면 |
| **API 문서** | `https://api.sam.kr/api-docs/index.html` | Swagger UI |
| **관리자 패널** | `https://mng.sam.kr` | MNG 관리자 화면 |
| **관리자 (별칭)** | `https://admin.sam.kr` | MNG와 동일 |
| **디자인 시스템** | `https://design.sam.kr` | 컴포넌트 스토리북 |
| **레거시** | `https://5130.sam.kr` | 기존 PHP 시스템 |

### 8-2. DB 접속 정보

| 항목 | 값 |
|------|-----|
| Host | `127.0.0.1` |
| Port | `3306` |
| Database | `samdb` |
| Username | `samuser` |
| Password | `sampass` |
| Root Password | `root` |
| 레거시 DB | `chandj` |

> DBeaver, DataGrip, MySQL Workbench 등 원하는 DB 클라이언트로 접속 가능

### 8-3. 접속 테스트

```bash
# API 응답 확인
curl -k https://api.sam.kr/api-docs/index.html

# MySQL 접속 확인
docker exec sam-mysql-1 mysql -u samuser -psampass -e "SHOW DATABASES;"
```

---

## 9. 저장소별 상세 정보

### 9-1. api/ (REST API 서버)

| 항목 | 값 |
|------|-----|
| 프레임워크 | Laravel 12 (PHP 8.4) |
| 인증 | Sanctum (토큰 기반) |
| API 문서 | Swagger (l5-swagger) |
| DB | MySQL 8.0 (samdb) |
| 역할 | 모든 비즈니스 로직의 중심, 프론트/관리자 모두 이 API 사용 |

**주요 명령어:**

```bash
# 컨테이너 진입
docker exec -it sam-api-1 bash

# 마이그레이션
php artisan migrate
php artisan migrate:status

# Swagger 재생성
php artisan l5-swagger:generate

# 코드 포매터
./vendor/bin/pint

# 캐시 초기화
php artisan cache:clear
php artisan config:clear
php artisan route:clear
```

**핵심 디렉토리:**

```
api/
├── app/
│   ├── Http/Controllers/Api/V1/   ← API 컨트롤러
│   ├── Http/Requests/             ← FormRequest (검증)
│   ├── Models/                    ← Eloquent 모델
│   ├── Services/                  ← 비즈니스 로직 (핵심)
│   ├── Swagger/v1/                ← Swagger 문서 클래스
│   └── Helpers/ApiResponse.php    ← 응답 헬퍼
├── database/migrations/           ← DB 마이그레이션
├── routes/api.php                 ← API 라우트
└── lang/ko/                       ← 한국어 메시지
```

### 9-2. mng/ (관리자 패널)

| 항목 | 값 |
|------|-----|
| 프레임워크 | Laravel 12 (PHP 8.4) |
| 프론트엔드 | Blade + Tailwind CSS + HTMX + DaisyUI |
| 역할 | 시스템 관리, 메뉴/권한 관리, 테넌트 관리 |

**주요 명령어:**

```bash
# 컨테이너 진입
docker exec -it sam-mng-1 bash

# 프론트 에셋 빌드
npm run build       # 프로덕션
npm run dev         # 개발 (HMR)

# 코드 포매터
./vendor/bin/pint
```

**핵심 디렉토리:**

```
mng/
├── app/
│   ├── Http/Controllers/          ← 웹 컨트롤러
│   ├── Models/                    ← 독립 모델 (API와 별개)
│   └── Services/                  ← 비즈니스 로직
├── resources/views/               ← Blade 템플릿
├── routes/web.php                 ← 웹 라우트
└── public/                        ← 정적 파일
```

> **주의**: MNG에서는 DB 마이그레이션을 생성/실행하지 않습니다. DB 변경은 반드시 API 프로젝트에서 수행합니다.

### 9-3. react/ (Next.js 프론트엔드)

| 항목 | 값 |
|------|-----|
| 프레임워크 | Next.js 15 (React 19, TypeScript) |
| 스타일링 | Tailwind CSS 4 |
| UI 라이브러리 | shadcn/ui (Radix UI 기반) |
| 상태관리 | Zustand |
| 폼 검증 | Zod + React Hook Form |
| 역할 | 사용자용 ERP 프론트엔드 |

**주요 명령어:**

```bash
# React는 Docker 컨테이너가 자동으로 dev 서버를 실행합니다.
# 수동 실행이 필요한 경우:
docker exec -it sam-react-1 sh

npm run dev         # 개발 서버
npm run build       # 프로덕션 빌드
npm run lint        # ESLint
```

**핵심 디렉토리:**

```
react/
├── src/
│   ├── app/                       ← Next.js App Router 페이지
│   ├── components/
│   │   ├── ui/                    ← shadcn/ui 원자 컴포넌트
│   │   ├── molecules/             ← 조합 컴포넌트
│   │   ├── organisms/             ← 복합 컴포넌트
│   │   └── [도메인]/              ← 도메인별 컴포넌트
│   ├── lib/                       ← 유틸리티, API 헬퍼
│   └── stores/                    ← Zustand 스토어
├── public/                        ← 정적 파일
└── next.config.ts                 ← Next.js 설정
```

**핵심 규칙:**
- 모든 페이지는 `'use client'` 선언 필수 (폐쇄형 ERP, SSR 불필요)
- API 호출은 Server Action을 통해 수행 (HttpOnly 쿠키 프록시)
- `buildApiUrl()` 유틸리티 필수 사용

### 9-4. docs/ (기술 문서)

| 항목 | 값 |
|------|-----|
| 내용 | 개발 가이드, API 스펙, 기획 문서, 표준 |
| 형식 | Markdown |
| 설치 | 없음 (문서만 관리) |

```
docs/
├── INDEX.md                       ← 문서 인덱스 (여기부터 시작)
├── dev/
│   ├── dev_plans/                 ← 개발 계획 문서
│   ├── standards/                 ← 코드/아키텍처 표준
│   ├── guides/                    ← 셋업/사용 가이드
│   ├── changes/                   ← 변경 로그
│   └── deploys/                   ← 배포 문서
├── features/                      ← 기능 스펙
├── frontend/                      ← 프론트엔드 API 스펙
├── rules/                         ← 비즈니스 규칙
└── system/                        ← 시스템 아키텍처
```

### 9-5. hotfix/ (테스트/QA)

| 항목 | 값 |
|------|-----|
| 내용 | E2E 테스트 결과, 버그 리포트, 테스트 케이스 |
| 형식 | Markdown + 스크린샷 |
| 설치 | 없음 (문서만 관리) |

```
hotfix/
├── e2e/                           ← E2E 테스트 정의
├── testcase/                      ← 테스트 케이스 문서
├── reports/                       ← 테스트 실행 결과
├── screenshots/                   ← 시각적 테스트 증거
├── research/                      ← 조사 자료
├── sam.code-workspace             ← VS Code 워크스페이스
├── *-Test-Report_*.md             ← 개별 테스트 보고서
└── Fail-*_*.md                    ← 실패 케이스 보고서
```

---

## 10. 주요 아키텍처 개념

### 10-1. 멀티테넌트

- 모든 데이터에 `tenant_id` 컬럼이 존재
- `BelongsToTenant` 글로벌 스코프가 자동으로 테넌트 필터링
- 하나의 DB에서 여러 회사(테넌트)의 데이터를 격리

### 10-2. 데이터 흐름

```
[사용자 브라우저]
    │
    ▼
[Next.js (react/)]  ──Server Action──▶  [Laravel API (api/)]  ──▶  [MySQL]
                                              │
[관리자 브라우저]                                │
    │                                          │
    ▼                                          │
[Laravel MNG (mng/)]  ──내부 API 호출──────────┘
```

### 10-3. 인증 방식

| 클라이언트 | 인증 방식 | 설명 |
|-----------|----------|------|
| React (웹) | Sanctum Cookie | HttpOnly 쿠키, Server Action 프록시 |
| MNG (관리자) | 세션 + 내부 HMAC | Laravel 세션 + API 내부 통신 키 |
| 외부 연동 | API Key + Bearer | Sanctum 토큰 |

---

## 11. 자주 쓰는 명령어

### Docker 관련

```bash
# 전체 시작/종료
cd docker && docker compose up -d
cd docker && docker compose down

# 컨테이너 재시작
docker restart sam-api-1
docker restart sam-react-1

# 이미지 재빌드 (Dockerfile 수정 후)
docker compose build --no-cache api
docker compose up -d api

# 볼륨 포함 완전 초기화 (⚠️ DB 데이터 삭제됨)
docker compose down -v
```

### API 개발

```bash
docker exec sam-api-1 php artisan migrate              # 마이그레이션
docker exec sam-api-1 php artisan migrate:rollback      # 롤백
docker exec sam-api-1 php artisan l5-swagger:generate   # Swagger 재생성
docker exec sam-api-1 ./vendor/bin/pint                 # 코드 포매팅
docker exec sam-api-1 php artisan tinker                # REPL
docker exec sam-api-1 php artisan route:list            # 라우트 목록
```

### MNG 개발

```bash
docker exec sam-mng-1 php artisan tinker
docker exec sam-mng-1 npm run dev                       # Vite HMR
docker exec sam-mng-1 npm run build                     # 에셋 빌드
docker exec sam-mng-1 ./vendor/bin/pint
```

### MySQL 접속

```bash
# CLI 접속
docker exec -it sam-mysql-1 mysql -u samuser -psampass samdb

# 특정 쿼리 실행
docker exec sam-mysql-1 mysql -u samuser -psampass samdb -e "SHOW TABLES;"
```

---

## 12. 트러블슈팅

### SSL 인증서 오류 (브라우저에서 "안전하지 않음")

```bash
# mkcert CA가 설치되지 않은 경우
mkcert -install

# 인증서 재생성
cd docker/nginx/ssl
mkcert "*.sam.kr" localhost 127.0.0.1 ::1
mv _wildcard.sam.kr+3.pem sam.kr.crt
mv _wildcard.sam.kr+3-key.pem sam.kr.key

# Nginx 재시작
docker restart sam-nginx-1
```

### 도메인 접속이 안 될 때

```bash
# hosts 파일 확인
cat /etc/hosts | grep sam

# DNS 캐시 초기화 (macOS)
sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

# Nginx 설정 유효성 확인
docker exec sam-nginx-1 nginx -t
```

### 포트 포워딩 확인 (443 → 4443)

```bash
# 현재 규칙 확인
sudo pfctl -s rules | grep 4443

# 규칙 재적용
sudo pfctl -ef docker/nginx/ssl/pf-sam.conf

# 규칙 비활성화 (필요 시)
sudo pfctl -d
```

### DB 접속 오류

```bash
# MySQL 컨테이너 상태 확인
docker compose ps mysql

# MySQL 로그 확인
docker compose logs mysql

# 수동 접속 테스트
docker exec sam-mysql-1 mysql -u root -proot -e "SELECT 1;"
```

### React 빌드/HMR 오류

```bash
# node_modules 초기화
docker exec sam-react-1 rm -rf node_modules .next
docker exec sam-react-1 npm install
docker restart sam-react-1
```

### composer/npm 의존성 문제

```bash
# API
docker exec sam-api-1 composer install --no-cache
docker exec sam-api-1 composer dump-autoload

# MNG
docker exec sam-mng-1 composer install --no-cache
docker exec sam-mng-1 npm ci
```

---

## 13. Git 워크플로우

### 브랜치 전략

| 브랜치 | 역할 | 규칙 |
|--------|------|------|
| `main` | 배포용 | squash merge로만 올림, 직접 커밋 금지 |
| `develop` | 일상 작업 | 자유롭게 커밋 |
| `feature/*` | 대형 기능 | 선택적 사용 (1주일+ 작업) |

### 커밋 메시지 형식

```
type: 간결한 설명

type 종류:
  feat:     새 기능
  fix:      버그 수정
  refactor: 리팩토링
  docs:     문서
  chore:    설정, 빌드
  style:    코드 포매팅
```

### 일상 워크플로우

```bash
# 1. 작업 시작
cd api
git checkout develop
git pull origin develop

# 2. 작업 수행 + 커밋
git add <파일들>
git commit -m "feat: 수주관리 삭제 기능 추가"

# 3. 푸시
git push origin develop
```

---

## 14. 셋팅 체크리스트

아래 항목을 순서대로 확인하며 셋팅을 완료합니다.

- [ ] Docker Desktop 설치 및 실행
- [ ] Git 설치 및 Gitea 계정 발급
- [ ] 5개 저장소 클론 완료
- [ ] `/etc/hosts` 파일 수정
- [ ] mkcert 설치 + SSL 인증서 생성
- [ ] pfctl 포트 포워딩 설정
- [ ] api/.env 설정 (`.env.example` → `.env`)
- [ ] mng/.env 설정
- [ ] react/.env.local 설정
- [ ] API Key, HMAC Key 등 민감 값 입력 (팀 공유 문서 참조)
- [ ] `docker compose up -d --build` 실행
- [ ] 초기 설정 실행 (key:generate, composer install, migrate)
- [ ] `https://api.sam.kr/api-docs/index.html` 접속 확인
- [ ] `https://mng.sam.kr` 접속 확인
- [ ] `https://dev.sam.kr` 접속 확인
- [ ] DB 클라이언트로 `127.0.0.1:3306` 접속 확인

---

## 15. 참고 문서

| 문서 | 경로 | 설명 |
|------|------|------|
| 문서 인덱스 | `docs/INDEX.md` | 전체 문서 목록 |
| API 규칙 | `API_RULES.md` | API 개발 규칙 |
| 개발 명령어 | `DEV_COMMANDS.md` | 자주 쓰는 명령어 모음 |
| 품질 체크리스트 | `QUALITY_CHECKLIST.md` | 코드 품질 체크 항목 |
| 빠른 참조 | `SAM_QUICK_REFERENCE.md` | 핵심 규칙 요약 |
| SSL 가이드 | `docker/nginx/ssl/SSL_SETUP_GUIDE.md` | SSL 상세 설정 |

---

> 문의사항은 팀 슬랙 채널 또는 팀장에게 문의하세요.