Skip to content

Feat/prod server#123

Merged
selnem merged 4 commits into
developfrom
feat/prod-server
Jun 15, 2026
Merged

Feat/prod server#123
selnem merged 4 commits into
developfrom
feat/prod-server

Conversation

@selnem

@selnem selnem commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

✨ 작업 개요

프로덕션 서버 배포 파이프라인 구축 및 prod 환경 운영에 필요한 인프라·API 노출 정책을 정리했습니다.

주요 변경 사항

1. Prod CI/CD 추가 (prod_deploy.yml)

  • prod 브랜치(또는 test/CICD) PR 머지 시 Elastic Beanstalk(issueissyu-ai-prod / issueissyu-ai-env)에 자동 배포
  • PROD_ENV, AWS_PROD_ACCESS_KEY, AWS_PROD_SECRET_KEY 시크릿 기반 .env 생성 후 배포 패키지 zip 업로드
  • workflow_dispatch로 수동 배포도 지원

2. CDN URL 지원

  • CDN_ENABLED, CDN_BASE_URL 환경변수 추가
  • S3Util 공개 URL 생성 시 CDN 활성화 시 CDN base URL 우선 사용, 비활성/미설정 시 기존 S3 URL fallback

3. HNSW 벡터 인덱스 파라미터 환경변수화

  • VECTOR_HNSW_M, VECTOR_HNSW_EF_CONSTRUCTION, VECTOR_HNSW_EF_SEARCH, VECTOR_HNSW_DIST_METHOD 추가
  • VectorStoreService, insert_chunks_to_vector 스크립트가 build_hnsw_kwargs()로 동일한 HNSW 설정 사용

4. Prod Swagger 노출 라우터 정리

  • prod에서 비활성화: complaint-email, geo, festival-pins, contest-pins, policy-pins, test, vector-test
  • prod에서 유지/활성화: users, issue, complaint-apply, festival-admin, contest-admin, policy-admin
  • contest-admin, policy-admin은 prod에서도 노출되도록 disabled_envs 조정

5. 테스트 엔드포인트 보강 (TestRoute)

  • S3 임의 파일 업로드/다운로드/삭제 API 추가 (dev 전용, prod 비활성)

커밋 내역

커밋 내용
074039e CDN 설정 추가
214e772 HNSW 인덱스 파라미터 환경변수 분리
53cd244 prod Swagger 노출 라우터 정리
6f5b155 prod 서버 CD 추가

selnem added 4 commits June 15, 2026 20:51
VECTOR_HNSW_M, EF_CONSTRUCTION, EF_SEARCH, DIST_METHOD 설정을 추가하고
VectorStoreService와 벡터 insert 스크립트가 동일한 hnsw_kwargs를 사용하도록 통일한다.
prod 환경에서 complaint-email·geo·*-pins API를 숨기고 contest/policy-admin은 노출하도록 ROUTER_REGISTRY를 조정한다. 환경별 라우터 집합 회귀 방지 테스트를 추가한다.
@selnem selnem self-assigned this Jun 15, 2026
@selnem selnem added ⚙️ setting 프로젝트/환경 설정 (yml, CI, Gradle 설정 변경 등) ♻️ refactor 기능 변화 없는 코드 리팩터링 labels Jun 15, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces CDN configuration options (cdn_enabled, cdn_base_url) to serve S3 assets via a CDN, and exposes PostgreSQL HNSW index parameters (m, ef_construction, ef_search, dist_method) to customize vector search indexing. It also adds test endpoints for S3 operations (upload, download, delete) and updates environment-specific router registries. The review feedback suggests validating the CDN base URL format, adding warning logs for CDN misconfigurations, properly encoding non-ASCII filenames in the Content-Disposition header for downloads, and merging custom HNSW parameters with default values to prevent missing configuration keys.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread app/core/config.py
Comment on lines +414 to +419
@field_validator("cdn_base_url", mode="before")
@classmethod
def _empty_cdn_base_url_to_none(cls, value: object) -> object | None:
if isinstance(value, str) and value.strip() == "":
return None
return value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

CDN_BASE_URL이 설정되었을 때, 올바른 URL 형식(예: http:// 또는 https://로 시작)인지 검증하는 로직을 추가하는 것이 안전합니다. 잘못된 형식으로 설정되면 서비스 전체에서 생성되는 퍼블릭 URL이 깨질 수 있습니다.

    @field_validator("cdn_base_url", mode="before")
    @classmethod
    def _empty_cdn_base_url_to_none(cls, value: object) -> object | None:
        if isinstance(value, str):
            val = value.strip()
            if val == "":
                return None
            if not val.startswith(("http://", "https://")):
                raise ValueError("CDN_BASE_URL must start with http:// or https://")
            return val
        return value

Comment thread app/utils/S3Util.py
Comment on lines +108 to +110
cdn_base_url = (settings.cdn_base_url or "").strip()
if settings.cdn_enabled and cdn_base_url:
return f"{cdn_base_url.rstrip('/')}/{encoded_key}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

CDN_ENABLED가 True로 설정되어 있지만 CDN_BASE_URL이 비어 있는 경우, 의도치 않은 설정 오류일 가능성이 높습니다. 이 경우 S3 URL로 폴백하기 전에 경고 로그를 남겨두면 운영 환경에서 문제를 디버깅하는 데 큰 도움이 됩니다.

Suggested change
cdn_base_url = (settings.cdn_base_url or "").strip()
if settings.cdn_enabled and cdn_base_url:
return f"{cdn_base_url.rstrip('/')}/{encoded_key}"
cdn_base_url = (settings.cdn_base_url or "").strip()
if settings.cdn_enabled:
if cdn_base_url:
return f"{cdn_base_url.rstrip('/')}/{encoded_key}"
logger.warning("CDN_ENABLED is True but CDN_BASE_URL is not configured. Falling back to S3 URL.")

Comment thread app/routes/TestRoute.py
Comment on lines +60 to +65
filename = Path(key).name or "download.bin"
return StreamingResponse(
io.BytesIO(data),
media_type=content_type or "application/octet-stream",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

다운로드할 파일명에 한글 등 비ASCII 문자가 포함되어 있을 경우, Content-Disposition 헤더의 filename="..." 포맷은 브라우저에 따라 파일명이 깨지거나 헤더 파싱 에러를 유발할 수 있습니다. RFC 5987 표준에 따라 filename*=utf-8''... 포맷을 사용하고 파일명을 퍼센트 인코딩(urllib.parse.quote)하는 것이 안전합니다.

Suggested change
filename = Path(key).name or "download.bin"
return StreamingResponse(
io.BytesIO(data),
media_type=content_type or "application/octet-stream",
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)
from urllib.parse import quote
filename = Path(key).name or "download.bin"
encoded_filename = quote(filename)
return StreamingResponse(
io.BytesIO(data),
media_type=content_type or "application/octet-stream",
headers={"Content-Disposition": f"attachment; filename*=utf-8''{encoded_filename}"},
)

Comment on lines +63 to +64
self._hnsw_kwargs = hnsw_kwargs or dict(DEFAULT_HNSW_KWARGS)
logger.info("Vector HNSW config: %s", self._hnsw_kwargs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

hnsw_kwargs가 제공될 때 DEFAULT_HNSW_KWARGS를 완전히 대체(override)하게 됩니다. 만약 호출부에서 일부 키(예: {"hnsw_m": 24})만 전달할 경우, 나머지 필수 설정 키들이 누락되어 에러가 발생하거나 기본값이 적용되지 않을 수 있습니다. 기본값 딕셔너리와 입력받은 딕셔너리를 병합(merge)하도록 수정하는 것이 안전합니다.

Suggested change
self._hnsw_kwargs = hnsw_kwargs or dict(DEFAULT_HNSW_KWARGS)
logger.info("Vector HNSW config: %s", self._hnsw_kwargs)
self._hnsw_kwargs = {**DEFAULT_HNSW_KWARGS, **(hnsw_kwargs or {})}
logger.info("Vector HNSW config: %s", self._hnsw_kwargs)

@selnem selnem merged commit 94e3d28 into develop Jun 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

♻️ refactor 기능 변화 없는 코드 리팩터링 ⚙️ setting 프로젝트/환경 설정 (yml, CI, Gradle 설정 변경 등)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant