diff --git a/README.md b/README.md
index 47dfb559..ce8d02ac 100644
--- a/README.md
+++ b/README.md
@@ -1,187 +1,209 @@
# Beach Complex
-공공데이터를 바탕으로 전국 해수욕장 정보를 조회하고, 상태 정보, 즐겨찾기, 예약, 알림 기능까지 함께 제공하는 해수욕장 서비스 프로젝트입니다.
-이 레포는 Spring Boot 백엔드 애플리케이션의 원본이며, 프론트엔드와 문서는 별도 레포에서 관리합니다.
+**부산 해수욕장 7곳의 시설·혼잡도를 조회하고, 즐겨찾기와 방문 예약을 관리하는 서비스입니다.**
-## 프로젝트 개요
+[](https://github.com/Beach-complex/Beach_complex/actions/workflows/ci.yml)
+[](#품질과-배포)
-- 프로젝트 성격: 팀 프로젝트
-- 목적: 해수욕장 관련 공공데이터와 사용자 기능을 한 서비스 안에서 제공
-- 주요 기술: Spring Boot, PostgreSQL(PostGIS), Flyway, Docker Compose
-- 현재 구현 중심 흐름: 인증, 해수욕장 조회, 상태 정보 조회, 즐겨찾기, 예약, 알림
+Spring Boot 백엔드와 배포·모니터링 설정을 담은 저장소입니다.
-## 현재 상태
+[프론트엔드](https://github.com/Beach-complex/beach_complex_client) · [주요 구현](#주요-구현) · [문제 해결](#문제-해결) · [로컬 실행](#로컬-실행) · [문서](#문서)
-- 백엔드 API와 배포/테스트 설정이 구성된 저장소입니다.
-- 인증, 해수욕장 조회, 즐겨찾기, 예약, 알림 관련 API가 존재합니다.
-- DB 마이그레이션은 Flyway로 관리합니다.
-- React 프론트엔드는 [Beach-complex/frontend](https://github.com/Beach-complex/frontend), 협업/운영 문서는 [Beach-complex/docs](https://github.com/Beach-complex/docs)에서 관리합니다.
+## 기술 스택
-## 주요 기능
+
+
+
+
+
+
+
+
-### 사용자 기능
-- 회원가입, 로그인, JWT 기반 인증
-- 사용자별 즐겨찾기 등록, 삭제, 토글, 조회
+| 영역 | 기술 |
+| --- | --- |
+| 프론트엔드 | React (별도 저장소) |
+| 백엔드 | Java 21, Gradle, Spring Boot 3.3, Spring Security, JWT, Spring Data JPA / Hibernate |
+| 데이터베이스·캐시 | PostgreSQL 16 / PostGIS 3.4, Flyway, Caffeine, Redis 7 (로컬 개발용) |
+| 외부 연동 | Firebase Cloud Messaging(FCM), AWS Lambda Function URL / SigV4, Spring Mail / Gmail SMTP |
+| 메트릭·로그·Trace 계측 | Spring Boot Actuator, Micrometer, OpenTelemetry, Logback |
+| 모니터링 | Prometheus, node_exporter, Grafana Alloy, Loki, Tempo, Grafana |
+| 인프라 | AWS EC2, EBS, Security Group, IAM role, Docker / Docker Compose, Terraform, cloud-init |
+| CI/CD | GitHub Actions, GitHub Container Registry(GHCR), SSH 배포 |
+| 품질 | JUnit 5, Testcontainers, JaCoCo, Checkstyle, Spotless, Gitleaks |
-### 해수욕장 정보
-- 해수욕장 목록 조회
-- 해수욕장 상세 조회
-- 해수욕장 시설 정보 조회
-- 해수욕장 상태 정보 조회
+## 시스템 구성
-### 예약 및 알림
-- 예약 생성, 조회, 취소
-- 알림 설정 및 FCM 토큰 등록
-- 알림 테스트 API 제공
+[](./assets/images/system-architecture.png)
-## 기술 스택
+앱 서버는 Spring Boot와 PostgreSQL/PostGIS를 Docker Compose로 실행합니다. React 클라이언트는 REST API를 호출하고, 백엔드는 혼잡도 조회에 AWS Lambda, 이메일 인증에 Gmail SMTP, 푸시 알림에 FCM을 사용합니다.
-### Backend
-- Java 21
-- Spring Boot 3.3
-- Spring Security
-- Spring Validation
-- Spring Data JPA
-- Spring Data Redis
-- Spring Retry
-- Spring Mail
-- JWT
-- Swagger UI
-
-### Database / Infra
-- PostgreSQL
-- PostGIS
-- Flyway
-- Redis 설정
-- Caffeine Cache
-- Docker Compose
-
-### Quality
-- JUnit 5
-- Testcontainers
-- JaCoCo
-- Checkstyle
-- Spotless
-- GitHub Actions
+모니터링은 별도 EC2에 Prometheus·Tempo·Loki·Grafana를 두는 구성입니다. 앱의 Trace는 Tempo로, Alloy가 수집한 Docker 로그는 Loki로 보냅니다. Grafana에서 메트릭, 로그, Trace를 조회합니다.
-## 프로젝트 구조
+## 주요 구현
-```text
-Beach_complex/
-├── src/main/java/com/beachcheck/
-│ ├── auth/ # 인증, 토큰, 이메일 인증
-│ ├── beach/ # 해수욕장, 시설, 상태 정보
-│ ├── external/ # 외부 API 연동
-│ ├── global/ # 공통 설정, 예외, 보안, 유틸
-│ ├── notification/ # 알림
-│ ├── outbox/ # Outbox 이벤트 발행/전송
-│ ├── reservation/ # 예약
-│ └── user/ # 사용자, 즐겨찾기
-├── src/test/java/com/beachcheck/
-│ ├── auth/
-│ ├── beach/
-│ ├── external/
-│ ├── global/
-│ ├── notification/
-│ ├── outbox/
-│ ├── reservation/
-│ ├── support/ # 테스트 공통 설정/fixture
-│ └── user/
-├── src/main/resources/
-│ ├── application.yml
-│ ├── db/migration/
-│ └── firebase-service-account.json.example
-├── docs/ # 문서 레포 진입점
-├── .github/workflows/ # CI 및 자동화
-├── docker-compose.yml
-└── build.gradle
+### 반경 검색과 조회 캐시
+
+위도·경도와 반경을 입력하면 가까운 해수욕장부터 반환합니다. PostGIS의 `ST_DWithin`으로 검색 범위를 제한하고 `ST_Distance`로 정렬합니다. 반경은 API에서 km로 받아 DB 조회 시 m로 변환합니다.
+
+시설·상태 조회에는 Caffeine 캐시를 적용했습니다. 기존 메모리 맵에 만료 시간과 크기 제한이 없어 교체했으며, 별도 서버가 필요 없는 로컬 캐시를 선택했습니다. TTL은 10분, 캐시별 최대 항목은 1,000개입니다.
+
+[공간 쿼리](./src/main/java/com/beachcheck/beach/repository/BeachRepository.java) · [조회 API 통합 테스트](./src/test/java/com/beachcheck/beach/integration/BeachControllerIntegrationTest.java) · [Caffeine 선택 배경과 대안](https://github.com/Beach-complex/beach_complex_docs/blob/main/adr/001-cache-migration-caffeine.md)
+
+### Outbox 기반 푸시 알림
+
+알림과 전송할 이벤트를 하나의 DB 트랜잭션으로 저장합니다. API는 저장 직후 응답하고, 워커가 이벤트를 읽어 FCM으로 푸시를 보냅니다. 전송 결과와 재시도할 작업을 DB에 남겨 별도 메시지 브로커 없이 처리합니다.
+
+테스트 푸시 API의 처리 흐름은 다음과 같습니다.
+
+```mermaid
+sequenceDiagram
+ participant client
+ participant api
+ participant database
+ participant worker
+ participant fcm
+ client->>api: POST /api/notifications/test
+ api->>database: 알림과 Outbox, producer traceparent 저장
+ database-->>api: 단일 트랜잭션 커밋
+ api-->>client: 202 Accepted
+ worker->>database: 처리 대상 Outbox 조회 후 조회 트랜잭션 종료
+ database-->>worker: 이벤트 목록
+ worker->>database: 이벤트별 새 트랜잭션에서 알림 상태 확인
+ worker->>fcm: 푸시 전송
+ fcm-->>worker: 전송 결과
+ worker->>database: 성공 또는 재시도 또는 영구 실패 상태 저장
```
-## API 영역
+워커는 전송을 시도할 때마다 새 Trace를 만들고, 이벤트에 저장된 `traceparent`를 Span Link로 연결합니다. 이를 통해 재시도한 작업에서도 최초 요청을 찾을 수 있습니다. FCM 전송 후 DB 커밋에 실패하면 같은 알림이 다시 전송될 수 있습니다.
-현재 코드 기준으로 아래 컨트롤러가 구성되어 있습니다.
+[Outbox 설계와 대안](https://github.com/Beach-complex/beach_complex_docs/blob/main/adr/ADR-008-outbox-rdb-task-queue.md) · [전송·재시도 테스트](./src/test/java/com/beachcheck/outbox/service/OutboxEventDispatcherTest.java) · [Trace 연결 테스트](./src/test/java/com/beachcheck/outbox/tracing/OutboxTracingIntegrationTest.java)
-- `AuthController`
-- `BeachController`
-- `BeachConditionController`
-- `BeachFacilityController`
-- `ReservationController`
-- `UserFavoriteController`
-- `NotificationController`
+### 로그와 Trace 조회
-## 테스트와 협업
+Grafana에서 로그의 `traceId`를 누르면 해당 요청의 Trace를 볼 수 있습니다. Trace에서도 같은 요청의 로그를 조회할 수 있도록 Loki와 Tempo 데이터소스를 연결했습니다.
-- 단위 테스트와 통합 테스트를 함께 운영합니다.
-- Testcontainers를 사용해 데이터베이스 연동 테스트를 검증합니다.
-- GitHub Actions에서 빌드, 테스트, 커버리지 리포트 업로드를 수행합니다.
-- 협업 규칙, 기술 결정 기록, 트러블슈팅 문서는 `Beach-complex/docs` 레포에서 관리합니다.
-- `.github/ISSUE_TEMPLATE`, `.github/PULL_REQUEST_TEMPLATE`, `.github/pull_request_template.md`로 이슈와 PR 양식을 관리합니다.
+로그 수집은 Alloy가 맡습니다. 앱이 표준 출력으로 남긴 로그를 Docker 파일에서 읽어 Loki로 보내므로, 앱에 로그 전송 코드를 넣거나 Docker socket을 열 필요가 없습니다. 로그 파일의 회전 한도를 넘기거나 저장 볼륨에 장애가 생기면 로그가 유실될 수 있습니다.
-## 실행 방법
+JWT·FCM 토큰·SQL 바인딩 값은 Trace에 기록하지 않습니다. `traceId`는 로그 본문에만 남기고 Loki 인덱스 라벨에서는 제외했습니다.
-### 요구 사항
-- JDK 21
-- Node.js 20+
-- Docker
+[Loki 선택 배경](https://github.com/Beach-complex/beach_complex_docs/blob/main/adr/ADR-014-logging-storage-backend.md) · [Alloy 선택 배경](https://github.com/Beach-complex/beach_complex_docs/blob/main/adr/ADR-015-log-collector.md) · [Trace 수집 계약](./docs/TRACING_CONTRACT.md)
-### 1. 저장소 클론
+## 문제 해결
-```bash
-git clone https://github.com/Beach-complex/Beach_complex.git
-cd Beach_complex
-```
+### Outbox 트랜잭션 간 잠금 충돌
-### 2. 인프라 실행
+폴링 트랜잭션이 잡은 행 잠금 때문에 전송 트랜잭션의 갱신이 멈추는 문제가 있었습니다. 폴링 루프에 `NOT_SUPPORTED`를 적용하고, 조회 트랜잭션이 끝난 뒤 이벤트별 전송 트랜잭션을 시작하도록 바꿨습니다. 조회 메서드의 read-only 설정과 쓰기 잠금이 충돌하는 문제도 함께 수정했습니다.
-```bash
-docker-compose up -d postgres redis
-```
+통합 테스트에서는 준비 데이터를 먼저 커밋한 뒤 전송과 상태 변경을 확인합니다. 이미 전송한 알림을 건너뛰는 경우도 검사합니다.
+
+[재현·원인·수정 기록](https://github.com/Beach-complex/beach_complex_docs/blob/main/backend/troubleshooting/outbox-integration-requires-new-lock-readOnly-deadlock.md) · [통합 테스트](./src/test/java/com/beachcheck/outbox/integration/OutboxPublisherIntegrationTest.java)
+
+### Docker 로그 분할·회전 처리
+
+긴 JSON 로그가 여러 Docker 레코드로 나뉘고, Alloy가 멈춘 동안 로그 파일이 회전하면서 일부 기록이 누락되는 문제가 있었습니다.
+
+Alloy가 컨테이너와 출력 스트림별로 로그 조각을 합치도록 수정했습니다. 파일 지문과 읽기 위치도 저장해 재시작 후 이어 읽습니다. CI에서는 출력이 섞인 40KB JSON, 수집 중지 중 회전한 로그 110건, 파일 경계를 넘은 90KB JSON이 원문대로 복구되는지 확인합니다.
+
+[CI 검증 결과](./assets/reports/log-pipeline-20261001.json) · [검증 스크립트](./deploy/observability/scripts/verify-log-pipeline.py) · [운영 제약](./deploy/observability/README.md#운영-제약)
-### 3. 백엔드 실행
+그 밖의 기록: [Docker Engine과 Testcontainers의 API 버전 불일치 해결](https://github.com/Beach-complex/beach_complex_docs/blob/main/backend/troubleshooting/troubleshooting-testcontainers-docker-api-version-400.md)
+
+## 품질과 배포
+
+`main`·`develop` 대상 PR과 push에서 [CI](./.github/workflows/ci.yml)를 실행합니다.
+
+| 검증 대상 | 검사 내용 |
+| --- | --- |
+| 백엔드 | UTF-8, 컴파일·Checkstyle·Spotless, 단위·통합 테스트, 빌드, JaCoCo |
+| 관측 환경 | Terraform·cloud-init·Compose 설정, 합성 로그·Trace 전송, 회전·재시작·Loki 장애 복구 |
+
+DB 쿼리와 API 테스트는 Testcontainers의 PostgreSQL/PostGIS에서 실행합니다. Trace 테스트에서는 [SQL 바인딩 값이 수집되지 않는지](./src/test/java/com/beachcheck/global/tracing/JdbcTracingIntegrationTest.java), [외부 호출까지 Trace가 이어지는지](./src/test/java/com/beachcheck/external/congestion/CongestionClientTracingIntegrationTest.java) 검사합니다.
+
+커버리지 배지는 2026-10-01 커밋 `af6dceda` 기준이며 자동 갱신되지 않습니다. 라인 커버리지는 89.8% (1,617 / 1,801)입니다. [CI 결과](https://github.com/Beach-complex/Beach_complex/actions/runs/36867196523) · [JaCoCo 보고서](./assets/reports/jacoco-20261001.xml)
+
+관측 스택은 [Prometheus·Tempo·Grafana의 EC2 실행과 연동](https://github.com/Beach-complex/Beach_complex/pull/238)을 확인했습니다. [Loki·Alloy 로그 파이프라인](https://github.com/Beach-complex/Beach_complex/pull/264)은 로컬 Docker에서 합성 로그와 Trace로 테스트했습니다.
+
+- **비밀정보 검사:** 설치한 로컬 [Git 훅](./.githooks/pre-commit)이 [Gitleaks](./scripts/secret-scan.sh)로 스테이징된 추가 줄을 검사합니다.
+- **배포:** `main`의 백엔드 관련 파일이 바뀌면 이미지를 빌드해 GHCR에 올리고, SSH로 EC2의 Compose 서비스를 갱신합니다. [CD 수동 실행](./.github/workflows/backend-cd.yml)도 지원합니다.
+
+## 로컬 실행
+
+JDK 21, Docker Compose, Git, OpenSSL이 필요합니다. 아래 Bash 명령은 이메일·푸시 발송을 끈 상태로 API를 실행합니다.
```bash
-./gradlew bootRun
-```
+git clone https://github.com/Beach-complex/Beach_complex.git
+cd Beach_complex
+docker compose up -d --wait postgres redis
-Windows PowerShell에서는 아래 명령을 사용합니다.
+export SPRING_PROFILES_ACTIVE=dev
+export APP_FIREBASE_ENABLED=false
+export APP_OUTBOX_POLLING_ENABLED=false
+export APP_MAIL_ENABLED=false
+export MANAGEMENT_HEALTH_MAIL_ENABLED=false
+export CONGESTION_SIGV4_ENABLED=false
+export JWT_SECRET=$(openssl rand -hex 32)
-```powershell
-.\gradlew.bat bootRun
+./gradlew bootRun
```
-### 4. 프론트엔드 실행
+DB 마이그레이션과 초기 데이터 입력은 앱 시작 시 Flyway가 처리합니다. 외부 혼잡도 API를 연결하지 않으면 30분마다 실행되는 갱신 작업에서 연결 실패 로그가 남을 수 있습니다.
+
+[Swagger UI](http://localhost:8080/swagger-ui.html) · [Health](http://localhost:8081/actuator/health)
```bash
-git clone https://github.com/Beach-complex/frontend.git
-cd frontend
-npm install
-npm run dev
+# 해운대 좌표 기준 반경 5km 검색
+curl 'http://localhost:8080/api/beaches?lat=35.1587&lon=129.1599&radiusKm=5'
+
+# Docker 실행 상태에서 테스트·빌드·커버리지 확인
+./gradlew test assemble jacocoTestReport
```
-### 5. 접속 주소
+PowerShell 실행 방법, JWT 키 변경 시 주의사항, Redis 실행 이유와 외부 서비스 설정은 [로컬 개발 가이드](./docs/LOCAL_DEVELOPMENT.md)를 참고하세요.
+
+## 프로젝트 구조
-- Frontend: `http://localhost:5173`
-- Backend API: `http://localhost:8080`
-- Swagger UI: `http://localhost:8080/swagger-ui.html`
+```text
+Beach_complex/
+├── src/main/java/com/beachcheck/
+│ ├── auth/ # JWT, 이메일 인증·발송
+│ ├── beach/ # 검색, 시설·상태, 혼잡도 갱신 스케줄러
+│ ├── external/ # 혼잡도 API, AWS SigV4
+│ ├── global/ # 보안, 캐시, 예외, 로그·Trace 설정
+│ ├── notification/ # 알림 생성·설정
+│ ├── outbox/ # 이벤트 저장·폴링·FCM 전송
+│ ├── reservation/ # 방문 예약
+│ └── user/ # 사용자, 즐겨찾기
+├── src/main/resources/
+│ ├── db/migration/ # Flyway 마이그레이션
+│ └── application*.yml
+├── src/test/ # 단위·통합 테스트, 공통 fixture
+├── deploy/
+│ └── observability/ # Terraform, Compose, 관측 파이프라인 검증
+├── docs/ # 로컬 개발 가이드, Trace 공통 계약
+├── assets/ # README 구성도·CI 검증 기록
+├── .github/workflows/ # CI, 컨테이너 빌드·배포
+└── docker-compose.yml # 로컬 PostgreSQL/PostGIS, Redis
+```
## 문서
-- 프로젝트 문서 홈: [Beach-complex/docs](https://github.com/Beach-complex/docs)
-- 로컬 문서 진입점: [`docs/README.md`](./docs/README.md)
-- SSOT 문서: [docs/ssot.md](https://github.com/Beach-complex/docs/blob/main/ssot.md)
-- ADR: [docs/adr](https://github.com/Beach-complex/docs/tree/main/adr)
-- 트러블슈팅 문서: [docs/troubleshooting/docs-README.md](https://github.com/Beach-complex/docs/blob/main/troubleshooting/docs-README.md)
-- 프론트엔드 레포: [Beach-complex/frontend](https://github.com/Beach-complex/frontend)
-- 프론트엔드 가이드: [docs/guides/backend-developer-frontend-guide.md](https://github.com/Beach-complex/docs/blob/main/guides/backend-developer-frontend-guide.md)
+이 저장소에는 공개용 개발 가이드와 일부 기술 문서를 두고 있습니다. 상세 설계와 내부 협업·운영 기록은 비공개 문서 저장소에서 관리합니다. 본문에 연결된 ADR과 문제 해결 기록도 해당 저장소의 접근 권한이 필요합니다.
+
+- [로컬 개발 가이드](./docs/LOCAL_DEVELOPMENT.md)
+- [문서 저장소 · 비공개](https://github.com/Beach-complex/beach_complex_docs) · [ADR 목록 · 비공개](https://github.com/Beach-complex/beach_complex_docs/tree/main/adr)
+- [문제 해결 기록 · 비공개](https://github.com/Beach-complex/beach_complex_docs/tree/main/backend/troubleshooting)
+- [관측 환경 가이드](./deploy/observability/README.md) · [Trace 공통 계약](./docs/TRACING_CONTRACT.md)
+- [프론트엔드](https://github.com/Beach-complex/beach_complex_client)
-## 팀
+## 팀 & 역할
| 역할 | 이름 | GitHub |
| --- | --- | --- |
| Backend / Infra | 박재홍 | [@PHJ2000](https://github.com/PHJ2000) |
-| Backend | 박건우 | [@GunwooPar](https://github.com/GunwooPar) |
+| Backend | 박건우 | [@geonusp](https://github.com/geonusp) |
| Frontend / PM | 정도경 | [@DoGyeong888](https://github.com/DoGyeong888) |
## 라이선스
-이 프로젝트는 [MIT](./LICENSE) 라이선스를 따릅니다.
+[MIT License](./LICENSE)
diff --git a/assets/badges/coverage.svg b/assets/badges/coverage.svg
new file mode 100644
index 00000000..30b3e43c
--- /dev/null
+++ b/assets/badges/coverage.svg
@@ -0,0 +1 @@
+
diff --git a/assets/images/system-architecture.png b/assets/images/system-architecture.png
new file mode 100644
index 00000000..5e219052
Binary files /dev/null and b/assets/images/system-architecture.png differ
diff --git a/assets/reports/jacoco-20261001.xml b/assets/reports/jacoco-20261001.xml
new file mode 100644
index 00000000..89a304bd
--- /dev/null
+++ b/assets/reports/jacoco-20261001.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/reports/log-pipeline-20261001.json b/assets/reports/log-pipeline-20261001.json
new file mode 100644
index 00000000..f09f6a74
--- /dev/null
+++ b/assets/reports/log-pipeline-20261001.json
@@ -0,0 +1,25 @@
+{
+ "checked_at_utc": "2026-10-01T13:16:28.322906+00:00",
+ "scope": "isolated local Docker; synthetic app logs and OTLP trace; not EC2 deployment or browser UI",
+ "checks": [
+ "Local Loki, Tempo, Grafana and Docker log emitters ready",
+ "Real Docker JSON is preserved; unrelated logs excluded; only four low-cardinality labels",
+ "Grafana datasource health and provisioned log-to-trace / trace-to-log targets resolve",
+ "Alloy persisted file fingerprints/offsets and resumed logs written during its stop",
+ "40 KB JSON preserved during live rotation with interleaved stdout/stderr from two containers",
+ "All 110 retained rotated records and a 90 KB JSON spanning files recovered after Alloy restart",
+ "Alloy discovers a recreated backend without a hardcoded container ID",
+ "Alloy retries delivery across a short Loki outage",
+ "Loki data survives container recreation; compactor retention active at 72h"
+ ],
+ "images": {
+ "loki": "grafana/loki:3.7.8",
+ "tempo": "grafana/tempo:3.0.2",
+ "grafana": "grafana/grafana:13.1.3",
+ "alloy": "grafana/alloy:v1.19.2",
+ "emitter": "python:3.12-slim",
+ "emitter2": "python:3.12-slim",
+ "ignored": "python:3.12-slim"
+ },
+ "production_deployed": false
+}
diff --git a/docs/LOCAL_DEVELOPMENT.md b/docs/LOCAL_DEVELOPMENT.md
new file mode 100644
index 00000000..f90d4fcc
--- /dev/null
+++ b/docs/LOCAL_DEVELOPMENT.md
@@ -0,0 +1,121 @@
+# 로컬 개발 가이드
+
+[README로 돌아가기](../README.md)
+
+## 1. 준비
+
+- JDK 21
+- Docker Engine 또는 Docker Desktop과 Docker Compose
+- Git
+
+백엔드는 저장소의 Gradle Wrapper로 실행합니다. Node.js는 프론트엔드를 함께 개발할 때 필요합니다.
+
+```bash
+git clone https://github.com/Beach-complex/Beach_complex.git
+cd Beach_complex
+docker compose up -d --wait postgres redis
+```
+
+기본 DB 접속 정보는 `localhost:15432`, 데이터베이스 `beach_complex`, 사용자·비밀번호 `beach`입니다. Redis 포트는 `6379`입니다. API 캐시는 Caffeine을 사용하지만 기본 Actuator Health에는 Redis 점검이 포함되어 있어, Redis를 실행하지 않으면 Health가 `503`을 반환합니다.
+
+## 2. 외부 인증정보 없이 백엔드 실행
+
+기본 설정은 Firebase와 메일 발송을 활성화합니다. 로컬에서 API를 먼저 확인하려면 아래처럼 비활성화해 실행하세요. Bash 예시는 JWT 키 생성에 OpenSSL을 사용합니다.
+
+```bash
+export SPRING_PROFILES_ACTIVE=dev
+export APP_FIREBASE_ENABLED=false
+export APP_OUTBOX_POLLING_ENABLED=false
+export APP_MAIL_ENABLED=false
+export MANAGEMENT_HEALTH_MAIL_ENABLED=false
+export CONGESTION_SIGV4_ENABLED=false
+export JWT_SECRET=$(openssl rand -hex 32)
+
+./gradlew bootRun
+```
+
+### Windows PowerShell
+
+```powershell
+$env:SPRING_PROFILES_ACTIVE = "dev"
+$env:APP_FIREBASE_ENABLED = "false"
+$env:APP_OUTBOX_POLLING_ENABLED = "false"
+$env:APP_MAIL_ENABLED = "false"
+$env:MANAGEMENT_HEALTH_MAIL_ENABLED = "false"
+$env:CONGESTION_SIGV4_ENABLED = "false"
+# 로컬 실행용 임시 키 생성
+$env:JWT_SECRET = [guid]::NewGuid().ToString("N") + [guid]::NewGuid().ToString("N")
+
+.\gradlew.bat bootRun
+```
+
+앱을 시작하면 Flyway가 마이그레이션과 초기 데이터를 적용합니다. 위 설정에서는 이메일과 푸시를 발송하지 않습니다.
+
+새 JWT 키로 앱을 다시 시작하면 이전에 발급한 액세스 토큰은 사용할 수 없습니다.
+
+혼잡도는 30분마다 갱신하며, 외부 API를 연결하지 않으면 갱신 시 연결 실패 로그가 남을 수 있습니다. `CONGESTION_SIGV4_ENABLED=false`는 요청 서명만 끄므로 스케줄러는 계속 실행됩니다.
+
+## 3. 동작 확인
+
+| 용도 | 기본 주소 |
+| --- | --- |
+| Backend API | |
+| Swagger UI | |
+| OpenAPI JSON | |
+| Health | |
+
+```bash
+# 해수욕장 목록
+curl http://localhost:8080/api/beaches
+
+# 해운대 좌표 기준 반경 5km 검색
+curl 'http://localhost:8080/api/beaches?lat=35.1587&lon=129.1599&radiusKm=5'
+```
+
+프론트엔드를 연결할 때 현재 Spring Security의 CORS 허용 출처는 `http://localhost:3000`과 `http://localhost:8080`입니다. 다른 포트를 사용한다면 [SecurityConfig](../src/main/java/com/beachcheck/global/security/SecurityConfig.java)의 설정을 맞춰야 합니다.
+
+## 4. 외부 서비스 연결
+
+| 연동 | 설정 |
+| --- | --- |
+| PostgreSQL | `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_USERNAME`, `SPRING_DATASOURCE_PASSWORD` |
+| 이메일 | `APP_MAIL_ENABLED=true`, `SPRING_MAIL_USERNAME`, `SPRING_MAIL_PASSWORD`; 메일 상태 점검 시 `MANAGEMENT_HEALTH_MAIL_ENABLED=true` |
+| 이메일 인증 링크 | `APP_EMAIL_VERIFICATION_BASE_URL` — 기본값 `http://localhost:8080/api/auth/verify-email` |
+| Firebase FCM | `APP_FIREBASE_ENABLED=true`, `APP_OUTBOX_POLLING_ENABLED=true`, 서비스 계정 설정 |
+| Firebase 서비스 계정 | `APP_FIREBASE_CREDENTIALS_JSON_BASE64` 또는 `APP_FIREBASE_CREDENTIALS_PATH` |
+| 혼잡도 API | `CONGESTION_BASE_URL`; AWS 연동 시 `CONGESTION_SIGV4_ENABLED=true`, `AWS_REGION`, 실행 환경의 AWS 자격증명 또는 `AWS_PROFILE` |
+| Trace 전송 | `MANAGEMENT_OTLP_TRACING_ENDPOINT` — 예: `http://localhost:4318/v1/traces` |
+| Trace 샘플링 | `MANAGEMENT_TRACING_SAMPLING_PROBABILITY` — 기본값 `0.1` |
+
+Trace endpoint를 지정하지 않으면 수집기 없이 실행할 수 있습니다. 외부 연동 값은 실행 환경에 주입하며, 로컬 예제의 JWT 키는 배포 환경에서 별도 비밀값으로 교체합니다. 전체 기본값은 [application.yml](../src/main/resources/application.yml), 배포 변수 예시는 [backend-ec2.env.example](../deploy/backend-ec2.env.example)에 있습니다.
+
+## 5. 테스트와 Git 훅
+
+통합 테스트에는 실행 중인 Docker 데몬이 필요합니다.
+
+```bash
+./gradlew compileJava checkstyleMain spotlessCheck
+./gradlew test assemble jacocoTestReport
+
+# 선택: 로컬 Git 훅 설치
+./gradlew installGitHooks
+```
+
+테스트 결과는 `build/reports/tests/test/index.html`, 커버리지는 `build/reports/jacoco/test/html/index.html`에 생성됩니다. 관측 테스트 도구와 실행 조건은 [검증 가이드](../deploy/observability/README.md#로컬-검증)를 참고하세요.
+
+## 6. API 참고
+
+실행 후 [Swagger UI](http://localhost:8080/swagger-ui.html)에서 요청·응답 스키마를 확인할 수 있습니다. 인증이 필요한 API에는 `Authorization: Bearer ` 헤더를 보냅니다. 반경 검색은 `lat`, `lon`, `radiusKm`을 모두 전달해야 합니다.
+
+| 영역 | 대표 경로 | 인증 |
+| --- | --- | --- |
+| 회원가입·로그인·토큰 갱신 | `POST /api/auth/signup`, `POST /api/auth/login`, `POST /api/auth/refresh` | 불필요 |
+| 내 정보·로그아웃 | `GET /api/auth/me`, `POST /api/auth/logout` | JWT |
+| 이메일 인증 | `GET /api/auth/verify-email`, `POST /api/auth/verify-email/confirm` | 인증 링크의 토큰 |
+| 해수욕장 검색 | `GET /api/beaches` | 불필요 |
+| 시설 조회 | `GET /api/beaches/{beachId}/facilities` | 불필요 |
+| 최근 상태 조회 | `GET /api/beaches/{beachId}/conditions/recent` | 불필요 |
+| 즐겨찾기 | `GET /api/favorites`, `POST /api/favorites/{beachId}` | JWT |
+| 예약 생성·목록 | `POST /api/beaches/{beachId}/reservations`, `GET /api/beaches/reservations` | JWT |
+| 알림 설정 저장 | `POST /api/notifications/fcm-token`, `PUT /api/notifications/settings` | JWT |
+| 테스트 푸시 발송 | `POST /api/notifications/test` | JWT |