Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
7fb2ef7
feat(frontend): ReLink 리브랜딩 반영 및 UI/UX 에셋 리뉴얼
jin2304 May 17, 2026
c79fed5
feat(infra): Docker 기반 환경별 인프라 구축 및 Nginx 프록시 고도화
jin2304 May 17, 2026
b6a115b
feat(backend): 폴더 및 하위 리소스 재귀적 Soft Delete 비즈니스 로직 구현
jin2304 May 17, 2026
45dd5ec
config(backend): 로컬/개발/운영 환경별 프로퍼티 설정 정비
jin2304 May 17, 2026
9493756
feat(deploy): GitHub Actions CI/CD 구축 및 인프라 아키텍처 명세 추가
jin2304 May 17, 2026
a249658
config(deploy): deploy-prod.yml 내 PR 단계 배포 트리거 추가
jin2304 May 17, 2026
308955c
config(deploy): 프론트엔드 CI/CD 빌드 및 배포 파이프라인 연동
jin2304 May 19, 2026
06ad3b1
refactor(infra): 백엔드 Docker 멀티 스테이지 빌드 적용 및 CI/CD 워크플로우 통일
jin2304 May 23, 2026
bf5e189
feat(backend): AI 링크 분석 시스템 컨텍스트 모드 적용 및 개인화 추천 품질 개선
jin2304 May 23, 2026
fc22d87
feat(backend): 감사 시각 타임존 UTC 고정 및 폴더 동시성 제어 적용
jin2304 May 25, 2026
827ea44
feat(infra): Nginx 일반 계정 권한 적용 및 보안 수준 강화
jin2304 May 25, 2026
2584fe0
style(frontend): 텍스트 선택 방지 적용 및 폰트 렌더링 최적화
jin2304 May 25, 2026
2dcd7fc
fix(frontend): 파비콘 3단계 폴백 시스템 구축 및 스피너 UI 개선
jin2304 May 25, 2026
e3cfff9
docs: 클라우드플레어 보안 격리 및 웹 인프라 구성 문서 갱신
jin2304 May 25, 2026
5de190a
refactor: HIGH_CONTEXT 판정 최소 폴더 기준 변경 (7개 -> 10개)
jin2304 May 25, 2026
3f608a5
config(deploy): 배포 트리거를 main 및 dev 브랜치 push로 제한 및 선제적 컨테이너 삭제 제거
jin2304 May 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.git
.gradle
.gradle-user
.gradle-user-home
.idea
.vscode
.claude
CLAUDE.md
build/
bin/
out/
frontend/
frontend_old/
docs_tmp/
status.txt
status_utf8.txt
prd.json
progress.txt
version.txt

# Environment variables (Secrets)
.env
.env.*
79 changes: 79 additions & 0 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: ReLink Production CI/CD

on:
push:
branches: [ "main", "dev" ] # main, dev 브랜치로 머지(push) 완료 시에만 실행

jobs:
build-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # 소스 코드 가져오기

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} # 도커 허브 로그인

- name: Build and Push Backend Docker Image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/relink-backend:latest
${{ secrets.DOCKERHUB_USERNAME }}/relink-backend:${{ github.sha }}

build-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4 # 소스 코드 가져오기

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} # 도커 허브 로그인

- name: Build and Push Frontend Docker Image
uses: docker/build-push-action@v5
with:
context: ./frontend
file: ./frontend/Dockerfile
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/relink-frontend:latest
${{ secrets.DOCKERHUB_USERNAME }}/relink-frontend:${{ github.sha }}

deploy:
needs: [build-backend, build-frontend] # 백엔드와 프론트엔드 빌드가 모두 성공해야 배포 시작
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4 # 설정 파일 복사를 위해 Checkout 추가

- name: Copy config files via SCP
uses: appleboy/scp-action@917f8b81dfc1ccd331fef9e2d61bdc6c8be94634 # v0.1.7
with:
host: ${{ secrets.PROD_VM1_HOST }}
username: ${{ secrets.PROD_VM1_USER }}
key: ${{ secrets.PROD_VM1_SSH_KEY }}
source: "deploy/prod/web/docker-compose-prod-web.yml,deploy/prod/web/nginx-prod.conf"
target: "~/Searchweb/deploy"
strip_components: 3

- name: Deploy to Oracle Cloud VM1
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
with:
host: ${{ secrets.PROD_VM1_HOST }}
username: ${{ secrets.PROD_VM1_USER }}
key: ${{ secrets.PROD_VM1_SSH_KEY }}
script: |
cd ~/Searchweb/deploy
# 서버에서 최신 프론트 및 백엔드 이미지로 교체 및 재시작
sudo IMAGE_TAG=${{ github.sha }} docker compose -f docker-compose-prod-web.yml pull
sudo IMAGE_TAG=${{ github.sha }} docker compose -f docker-compose-prod-web.yml up -d
# 오래된 이미지 파일 정리(용량 확보)
sudo docker image prune -f
92 changes: 0 additions & 92 deletions .github/workflows/deploy-test.yml

This file was deleted.

8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,10 @@ CLAUDE.md
.gradle-user/
status.txt
status_utf8.txt
docs/Backend/memo/
docs_tmp/




prd.json
progress.txt
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY . .

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude local env files from backend Docker build

When this image is built from a developer or deployment checkout that contains the gitignored .env, this broad COPY . . includes it because the new root .dockerignore does not exclude .env or .env.* (unlike the frontend one). That puts secrets into the build context and builder-layer history, which is especially risky with remote builders or exported caches; add the env patterns to .dockerignore or copy only the Gradle/project files needed for the jar.

Useful? React with 👍 / 👎.

RUN chmod +x gradlew && ./gradlew clean bootJar

FROM eclipse-temurin:17-jre-alpine
COPY build/libs/SearchWeb-0.0.1-SNAPSHOT.jar app.jar
COPY --from=builder /app/build/libs/*-SNAPSHOT.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
66 changes: 66 additions & 0 deletions deploy/dev/docker-compose-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# dev 환경: 운영(prod)과 동일하게 프론트(Nginx), 백엔드(App), DB를 모두 Docker로 실행.
services:
# (1) dev PostgreSQL 데이터베이스
postgres:
image: postgres:16
container_name: dev-db
environment:
POSTGRES_DB: ${DEV_POSTGRES_DB}
POSTGRES_USER: ${DEV_POSTGRES_USER}
POSTGRES_PASSWORD: ${DEV_POSTGRES_PASSWORD}
TZ: Asia/Seoul
ports:
- "7000:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
- ../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "[1] init_postgres.sql 후보 파일 검색"
fd -i "init_postgres.sql"

echo
echo "[2] dev compose가 참조하는 경로 존재 확인"
test -f src/main/resources/db/init_postgres.sql && echo "OK: src/main/resources/db/init_postgres.sql 존재" || echo "MISSING: src/main/resources/db/init_postgres.sql"

echo
echo "[3] prod DB compose 참조 파일 확인"
test -f deploy/prod/db/init_postgres.sql && echo "OK: deploy/prod/db/init_postgres.sql 존재" || echo "MISSING: deploy/prod/db/init_postgres.sql"

Repository: Searchweb-Dev/Searchweb-Back

Length of output: 325


DB 초기화 SQL 마운트 경로는 존재하므로 현재 컨테이너 기동 시 문제없습니다.

다만 dev와 prod 환경의 초기화 SQL 파일 위치가 다릅니다 (dev: src/main/resources/db/, prod: deploy/prod/db/). 향후 경로 기준을 통일하면 유지보수가 더 용이할 것입니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/dev/docker-compose-dev.yml` at line 16, 현재 dev 컨테이너의 DB 초기화 SQL 마운트는
'../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro'로
되어 있고 prod는 'deploy/prod/db/'에 파일이 있어 경로가 다릅니다; dev와 prod 모두 같은 기준 경로로 통일하도록
docker-compose-dev.yml의 마운트 경로(문자열
'../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro')
또는 prod의 디렉터리 구조를 조정해 하나의 위치로 합치고(예: 모두 src/main/resources/db 또는 모두
deploy/prod/db로 이동), 필요하면 관련 문서/README와 배포 스크립트에도 동일한 경로를 반영하거나 심볼릭 링크를 생성해
유지보수성을 확보하세요.

healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 10
networks:
- dev-searchweb-network

# (2) dev Spring Boot 백엔드
backend:
build:
context: ../../
dockerfile: Dockerfile
container_name: dev-app
ports:
- "8000:8080"
environment:
- SPRING_PROFILES_ACTIVE=dev
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/${DEV_POSTGRES_DB}
- SPRING_DATASOURCE_USERNAME=${DEV_POSTGRES_USER}
- SPRING_DATASOURCE_PASSWORD=${DEV_POSTGRES_PASSWORD}
env_file:
- ../../.env
depends_on:
postgres:
condition: service_healthy
networks:
- dev-searchweb-network

# (3) dev nginx 프론트 정적 서빙 및 백엔드 프록시
nginx:
image: nginx:alpine
container_name: dev-nginx
ports:
- "4000:80"
volumes:
- ../../frontend/out:/usr/share/nginx/html:ro
- ./nginx-dev.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- backend
networks:
- dev-searchweb-network

networks:
dev-searchweb-network:
driver: bridge

volumes:
postgres-data:
name: dev-postgres-data
43 changes: 43 additions & 0 deletions deploy/dev/nginx-dev.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
server {
listen 80;
server_name localhost;

# Next.js 정적 빌드 결과물을 서빙합니다.
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri.html $uri/ /index.html;
}

# dev Docker 네트워크의 Spring Boot 컨테이너로 API 요청을 전달합니다.
location /api/ {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# OAuth2 로그인 시작 요청을 Spring Boot 컨테이너로 전달합니다.
location /oauth2/ {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# OAuth2 provider callback 요청을 Spring Boot 컨테이너로 전달합니다.
location /login/oauth2/ {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
26 changes: 26 additions & 0 deletions deploy/local/docker-compose-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# local 환경: 프론트(pnpm dev), 백엔드(IDE)는 개별 실행, DB만 Docker로 실행.
services:
postgres:
image: postgres:16
container_name: local-db
environment:
POSTGRES_DB: ${DEV_POSTGRES_DB:-searchwebdb}
POSTGRES_USER: ${LOCAL_DB_USERNAME}
POSTGRES_PASSWORD: ${LOCAL_DB_PASSWORD}
TZ: Asia/Seoul
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
- ../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
networks:
- local-searchweb-network
restart: always

networks:
local-searchweb-network:
driver: bridge

volumes:
postgres-data:
name: local-postgres-data
28 changes: 28 additions & 0 deletions deploy/prod/db/docker-compose-prod-db.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: '3.8'

services:
# VM2 전용 PostgreSQL 데이터베이스입니다.
postgres:
image: postgres:16
container_name: relink-db
environment:
POSTGRES_DB: ${PROD_POSTGRES_DB}
POSTGRES_USER: ${PROD_DB_USERNAME}
POSTGRES_PASSWORD: ${PROD_DB_PASSWORD}
TZ: Asia/Seoul
env_file:
- ./.env
ports:
- "${PROD_VM1_INTERNAL_IP}:7000:5432"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind PostgreSQL to the DB host interface

Use the DB host’s own interface (or 0.0.0.0) for the published port instead of ${PROD_VM1_INTERNAL_IP}. In this compose file, postgres runs on VM2 but the host binding uses the VM1 variable, while the web compose already treats VM2 separately via ${PROD_VM2_INTERNAL_IP}; with normal env assignments this causes Docker to fail port publication because VM2 does not own VM1’s IP, so the production DB container cannot start.

Useful? React with 👍 / 👎.

volumes:
- postgres_data:/var/lib/postgresql/data
- ./init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 3s
retries: 10
restart: always

volumes:
postgres_data:
Loading