Skip to content

Conversation

@GoGangH
Copy link
Collaborator

@GoGangH GoGangH commented Feb 2, 2026

#️⃣연관된 이슈

ex) #이슈번호, #이슈번호

📝작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

스크린샷 (선택)

💬리뷰 요구사항(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 질문별 경험 매칭 기능 추가(유사도 점수 제공)
    • 프로젝트에 회사에서 원하는 인재상(company_talent) 및 프로젝트 목록에 질문 ID 참조 추가
    • 채팅 기록 응답에 오늘 남은 채팅 횟수(remaining_chats) 노출
  • 개선사항

    • 일일 채팅 한도 10→100 상향
    • 경험 기간을 단일 날짜에서 시작일/종료일 범위로 변경
    • 배포 안정성(블루/그린) 및 로깅/요청 시간 기록 강화

leegaarden and others added 25 commits January 23, 2026 22:53
## #️⃣연관된 이슈

> #16 

## 📝작업 내용

1. 채팅 히스토리 조회 - 커서 페이지네이션
2. 초안 생성 감지 -> function calling
3. 랭체인 내 DB 히스토리 사용 -> RunnableWithMessageHistory

평균 응답 속도: 5-7초 최대: 10초
(테스트 완료)

### 스크린샷 (선택)

## 💬리뷰 요구사항(선택)

페이지네이션 프론트 확인을 위해 우선 머지, 이후 제한량 부분 수정 예정

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * 채팅 메시지 조회에 커서 기반 페이지네이션 추가(next_cursor, has_more, 페이지 크기 지정 가능)
  * 데이터베이스에서 채팅 이력 로드하고 세션 내 캐시로 활용하는 메시지 히스토리 통합
  * 초안 여부 판별을 위한 자동 의도 분류 기능 도입

* **Improvements**
  * AI 응답 스트리밍 흐름 개선 및 안정성 강화
  * 채팅 이력 관리와 캐시 동작 최적화

* **Documentation**
  * API 문서에 페이지네이션 사용 예시 및 응답 스키마 보강

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## #️⃣연관된 이슈

> #15 

## 📝작업 내용

> 저번 PR에서 코드래빗 피드백 개선
1. 채팅 중 에러시에도 횟수 차감되는 문제
2. chat_id만 알면 답변 업데이트할 수 있는 권한 문제

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## 릴리스 노트

* **새로운 기능**
  * API 응답에 일일 남은 채팅 횟수(remaining_chats) 표시 추가
  * 채팅 전송 흐름에 남은 채팅 카운트 반영 및 실시간 업데이트 제공

* **버그 수정**
  * 채팅 내용 업데이트 시 소유권 검증 추가로 보안 강화

* **문서**
  * API 예시와 스웨거 문서에 남은 채팅 횟수 필드 반영

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## #️⃣연관된 이슈

> ex) #이슈번호, #이슈번호

## 📝작업 내용

> 태그 항목 수정

### 스크린샷 (선택)

## 💬리뷰 요구사항(선택)

> 리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요
>
> ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **업데이트**
* 시스템에서 사용 가능한 태그 범위가 크게 확대되었습니다. 이제 AI/LLM, API연동, 백엔드, DB설계 등 더욱 다양한
카테고리의 태그를 활용할 수 있습니다.
  * 관련 예제 데이터가 새로운 태그 형식에 맞춰 업데이트되었습니다.

* **테스트**
  * 새로운 태그 형식을 반영하도록 테스트 케이스가 업데이트되었습니다.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ments and disable FastAPI docs in production.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 2, 2026

Caution

Review failed

The pull request is closed.

Walkthrough

블루-그린 프로덕션 배포 스크립트 및 Compose 구성 추가, 경험 도메인 날짜 필드 분리(start_date/end_date)와 질문 기반 유사도 검색 기능 추가, 프로젝트에 company_talent 및 question_id 도입, 채팅 시스템에 레이트리미터 통합, 로깅/배포/테스트 관련 파일 대규모 추가·수정.

Changes

Cohort / File(s) Summary
배포 인프라 및 자동화
.github/workflows/deploy-prod.yml, scripts/deploy.sh, docker-compose.prod.yml, Makefile
블루-그린 배포 도입: scripts/deploy.sh로 init/deploy/rollback/status 구현, docker-compose.prod.yml에 blue/green 앱 및 서비스 정의, Makefile에 prod-* 타겟 추가, workflow가 deploy.sh 호출하도록 변경.
개발 환경 & 이미지
docker-compose.dev.yml, .gitignore, Dockerfile
dev Compose에 postgres/qdrant 포트 노출(5432,6333), .gitignoreCaddyfile.prod.active-color 추가, Dockerfile이 logging.ini를 이미지에 복사하도록 변경.
로깅 및 앱 초기화
logging.ini, src/main.py, src/config.py, src/database.py
logging.ini 추가, main에 요청 시간 로깅 미들웨어·환경 기반 OpenAPI/Docs 비활성화 로직 및 파일Config 로드 추가, CHAT_DAILY_LIMIT 10→100, SQLAlchemy echo 제거.
경험(Experience) 도메인
src/experience/models.py, src/experience/schemas.py, src/experience/service.py, src/experience/router.py, alembic/versions/..._initial_migration.py, tests/test_experience.py
datestart_date/end_date 분리, 질문 기반 유사도 검색 함수 및 GET /match-question/{question_id} 엔드포인트 추가, 관련 스키마(ExperienceWithQuestionSimilarity, ExperienceQuestionMatchResult) 추가, 마이그레이션에 company_talent 컬럼 추가, 테스트 보강 및 fixtures 추가.
프로젝트 도메인
src/projects/models.py, src/projects/schemas.py, src/projects/service.py
company_talent 필드 모델·스키마에 추가, ProjectListItemquestion_id 추가, get_projects가 ORM 객체 대신 dict 리스트 반환하고 첫 질문 ID를 포함하도록 변경(공개 반환 형식 변경).
채팅 시스템(레이트리미터 통합)
src/chats/router.py, src/chats/service.py, src/chats/schemas.py, src/chats/llm_service.py, src/chats/swagger.py
ChatHistoryResponse에 remaining_chats 추가, 스트리밍 경로에서 rate_limiter 전달로 변경(remaining_chats 직접 계산 제거), experience 블록 날짜 형식 프롬프트 변경, Swagger 예시 업데이트.
테스트
tests/test_experience.py
start_date/end_date로 테스트 데이터 전환, test_project/test_question 픽스처 추가, 질문기반 매칭 관련 유닛 테스트 추가(성공/실패 케이스).

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as Client
    participant Deploy as scripts/deploy.sh
    participant Inactive as logit-app-green
    participant Caddy as Caddy
    participant Active as logit-app-blue

    User->>Client: make prod-deploy
    Client->>Deploy: scripts/deploy.sh deploy
    Deploy->>Inactive: docker-compose build & up (target)
    Deploy->>Inactive: healthcheck polling
    Inactive-->>Deploy: 200 OK
    Deploy->>Deploy: Caddyfile 생성/대상 변경
    Deploy->>Caddy: caddy reload
    rect rgba(76,175,80,0.5)
        Client->>Caddy: 트래픽이 새 대상으로 라우팅됨
        Caddy->>Inactive: 요청 전달
    end
    Deploy->>Active: drain & stop old containers
    Deploy->>Deploy: .active-color 업데이트
    Deploy-->>User: deploy 완료
Loading
sequenceDiagram
    actor Client
    participant Router as 경험 라우터
    participant Service as experience.service
    participant DB as Database
    participant Embedding as Embedding API
    participant Qdrant as Qdrant

    Client->>Router: GET /match-question/{question_id}
    Router->>Service: get_experiences_with_question_similarity(question_id)
    Service->>DB: SELECT Question, Project by question_id
    DB-->>Service: Question, Project
    Service->>Service: compose query text (title+desc+company_talent)
    Service->>Embedding: generate embedding
    Embedding-->>Service: vector
    Service->>Qdrant: similarity search (filter user_id)
    Qdrant-->>Service: matched IDs + scores
    Service->>DB: fetch Experience rows by IDs
    DB-->>Service: Experience models
    Service-->>Router: list[(Experience, score)]
    Router-->>Client: ExperienceQuestionMatchResult JSON
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50분

Possibly related PRs

Suggested labels

feature

Suggested reviewers

  • leegaarden

주요 내용

이 PR은 프로덕션 배포 인프라 개선(블루-그린 배포), 경험 도메인의 날짜 필드 리팩토링(단일 date → start_date/end_date), 프로젝트 모듈에 회사 인재상 및 질문 ID 추가, 채팅 시스템의 속도 제한 통합, 그리고 종합적인 로깅 구성을 포함합니다.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목은 'Deploy: main 서버 배포'로, 배포 관련 작업을 나타내지만 구체적인 변경 사항을 충분히 설명하지 못합니다. 실제로는 blue-green 배포 전략 구현, 로깅 구조화, 경험-질문 유사도 API 추가, 프로젝트 모델 확장 등 광범위한 변경 사항이 포함되어 있습니다. PR 제목을 더 구체적으로 수정하시기 바랍니다. 예: 'Deploy: blue-green 배포 전략 및 로깅, API 기능 추가' 또는 주요 변경 사항을 명시하는 형태로 개선이 필요합니다.
Docstring Coverage ⚠️ Warning Docstring coverage is 79.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/chats/service.py (1)

75-155: ⚠️ Potential issue | 🟠 Major

레이트 리밋 증가가 done 이벤트에만 묶여 있어 우회 가능성

현재는 done 이벤트가 도달해야 increment 되므로 스트림 중단/연결 종료 시 카운트가 누락될 수 있습니다. 요청 단위로 제한을 적용하려면 사용자 메시지 저장 직후(또는 finally)로 이동하는 편이 안전합니다.

🛠️ 제안 diff
     # 4. 사용자 메시지 저장
     await create_user_chat(
         db=db,
         question=question,
         content=content,
         experience_ids=experience_ids,
     )
+    if rate_limiter:
+        await rate_limiter.increment(user_id)
 
     # 5. AI 응답 스트리밍 (RunnableWithMessageHistory가 DB에서 히스토리 자동 로드)
     full_content = ""
@@
             done_data = {
                 "type": "done",
                 "chat_id": str(ai_chat.id),
                 "is_draft": is_draft,
             }
             if rate_limiter:
-                await rate_limiter.increment(user_id)
                 remaining = await rate_limiter.get_remaining(user_id)
                 done_data["remaining_chats"] = remaining
🤖 Fix all issues with AI agents
In `@docker-compose.prod.yml`:
- Around line 98-100: 서비스의 ports: 설정에 있는 "5432:5432" 호스트 바인딩은 프로덕션에서 불필요하게 DB
포트를 외부에 노출합니다; 해당 DB 서비스의 ports 항목을 제거하거나(권장) 호스트 바인딩을 로컬로
제한("127.0.0.1:5432:5432" 형태)하거나 대신 내부 도커 네트워크만 사용하도록 설정하여 외부 접근을 차단하세요; 변경할 곳을
찾으려면 docker-compose.prod.yml에서 ports: 블록과 "5432:5432" 매핑을 수정하면 됩니다.
- Around line 130-132: The docker-compose file exposes Qdrant via the ports
mapping "- \"6333:6333\"", creating an unnecessary external attack surface;
either remove that ports entry so Qdrant is only reachable on the docker
network, or change the mapping to bind to localhost (e.g. "127.0.0.1:6333:6333")
so only the host can access it. Update the service block containing the ports
key (the "- \"6333:6333\"" entry) accordingly and ensure any internal services
connect via the compose network rather than the removed host port.

In `@logging.ini`:
- Around line 20-23: logger_uvicorn_access에서 handlers가 비어있고 propagate=0으로 설정되어
있어 uvicorn.access 접근 로그가 버려지고 있으니, logger_uvicorn_access
블록(qualname=uvicorn.access)을 수정해 handlers에 적절한 핸들러 이름(예: uvicorn_access 또는 기존
파일/console 핸들러 이름)을 추가하고/또는 level(예: INFO)도 명시하거나, 접근 로그를 상위로 전달하려면 propagate를
1로 변경하여 접근 로그가 출력되도록 하세요.

In `@Makefile`:
- Around line 91-103: The prod-deploy/prod-rollback/prod-status targets can fail
if scripts/deploy.sh is not executable; ensure deploy.sh is made executable
before invoking it by either (A) making those targets depend on prod-init (e.g.,
declare prod-deploy, prod-rollback, prod-status depend on prod-init so
prod-init's chmod +x runs first) or (B) adding a chmod +x scripts/deploy.sh line
at the start of each target. Update the Makefile targets prod-deploy,
prod-rollback, and prod-status (and keep prod-init) accordingly so
scripts/deploy.sh is guaranteed executable before ./scripts/deploy.sh is
executed.

In `@scripts/deploy.sh`:
- Around line 198-225: The rollback() function currently calls wait_for_healthy
without handling failures (so under set -e the script can exit silently); update
rollback() to check the return of wait_for_healthy and handle errors explicitly:
after calling wait_for_healthy "$target" add a conditional (or || handler) that
logs a clear error via log_error including the target and health check result,
attempts safe cleanup (stop the just-started "app-${target}" and/or stop both
app-${target} and app-${active} as appropriate), ensure Caddy is left in a
consistent state (call update_caddyfile/reload_caddy rollback or restore
previous config if possible), write an updated STATE_FILE reflecting the true
active color or a failure marker, and then exit with a non-zero code; reference
rollback(), wait_for_healthy, update_caddyfile, reload_caddy, STATE_FILE, and
DRAIN_WAIT when implementing.

In `@src/config.py`:
- Line 121: Confirm whether the CHAT_DAILY_LIMIT constant in config.py (symbol
CHAT_DAILY_LIMIT) should be 100 or the previous 10 by checking product
requirements and cost/infra assessments; if 100 is not intentional, revert the
constant to the approved default or make it configurable via an environment
variable (e.g., read CHAT_DAILY_LIMIT from env and fallback to default) and
update documentation; run a cost impact and Redis/DB capacity evaluation and
load test for the chosen limit; and add monitoring/metrics/alerts to track users
hitting CHAT_DAILY_LIMIT so we can observe real-world usage and adjust if
needed.

In `@src/experience/router.py`:
- Around line 230-241: The route definition for get_experiences_by_question
(path "/match-question/{question_id}") can be shadowed by the generic
"/{experience_id}" route because FastAPI matches routes in order; move the
entire get_experiences_by_question function (the `@router.get`(...) block for
"/match-question/{question_id}") so it appears before the router definition that
handles "/{experience_id}" (the generic experience detail route) to ensure
"/match-question/..." is matched first.
🧹 Nitpick comments (11)
src/questions/schemas.py (1)

80-88: 예시 값 일관성 유지 필요 (null vs 빈 문자열)
QuestionListResponse 예시는 answer: None인데, 여기서는 빈 문자열로 바뀌어 문서 소비자에게 의미가 혼동될 수 있습니다. 미응답을 null로 표현한다면 예시도 동일하게 맞추는 편이 좋습니다.

🔧 예시 일관성 수정 제안
-                "answer": "",
+                "answer": None,
docker-compose.dev.yml (1)

63-64: 호스트 포트 충돌을 줄이기 위해 포트 매핑 변수화 권장

Line 63-64, 95-96의 고정 포트 매핑은 로컬에 같은 포트가 이미 사용 중이면 dev 환경이 바로 실패합니다. 기본값을 두되 환경변수로 오버라이드 가능하게 만드는 편이 안전합니다.

♻️ 제안 diff
-    ports:
-      - "5432:5432"
+    ports:
+      - "${POSTGRES_PORT:-5432}:5432"
...
-    ports:
-      - "6333:6333"
+    ports:
+      - "${QDRANT_PORT:-6333}:6333"

Also applies to: 95-96

.github/workflows/deploy-prod.yml (2)

34-40: 배포 단계 실패 시 즉시 중단되도록 set -euo pipefail 추가 권장

Line 34-40의 스크립트는 중간 실패가 누락될 수 있어 배포 상태가 불명확해집니다. 엄격 모드를 추가해 실패를 조기에 감지하는 편이 안전합니다.

♻️ 제안 diff
           script: |
             cd ${{ secrets.PROD_APP_PATH }}
+            set -euo pipefail

56-58: 헬스체크 curl에 타임아웃 추가로 워크플로우 정체 방지

Line 58의 curl이 네트워크 이슈로 장시간 대기할 수 있습니다. 짧은 타임아웃을 두면 실패 원인이 더 명확해집니다.

♻️ 제안 diff
-            curl -f http://localhost:80/health || exit 1
+            curl -fsS --max-time 5 http://localhost:80/health || exit 1
src/main.py (2)

28-30: 상대 경로로 logging.ini를 로드하면 CWD에 따라 실패할 수 있습니다.

현재 작업 디렉토리가 애플리케이션 루트가 아닌 경우 FileNotFoundError가 발생할 수 있습니다. Docker 환경에서는 보통 WORKDIR이 고정되어 있어 문제가 없을 수 있지만, 명시적으로 절대 경로를 사용하면 더 안전합니다.

🔧 절대 경로 사용 제안
+from pathlib import Path
+
 # Load logging configuration
-fileConfig('logging.ini', disable_existing_loggers=False)
+_base_dir = Path(__file__).resolve().parent.parent
+fileConfig(_base_dir / 'logging.ini', disable_existing_loggers=False)
 logger = logging.getLogger(__name__)

115-133: 미들웨어 구현이 적절합니다. Ruff 제안에 따라 returnelse 블록으로 이동하면 가독성이 향상됩니다.

현재 구현도 정상 동작하지만, try 블록 내에서 return하는 것보다 else 블록을 사용하면 "예외가 발생하지 않은 경우에만 실행"이라는 의도가 더 명확해집니다.

♻️ try-else 패턴 적용 제안
 `@app.middleware`("http")
 async def logging_middleware(request: Request, call_next):
     """로그 미들웨어"""
     start_time = time.time()
     try:
         response = await call_next(request)
         process_time = (time.time() - start_time) * 1000
-        
+    except Exception as e:
+        process_time = (time.time() - start_time) * 1000
+        logger.error(
+            f'"{request.method} {request.url.path}" 500 {process_time:.2f}ms - Error: {e}',
+            exc_info=True
+        )
+        raise
+    else:
         logger.info(
             f'"{request.method} {request.url.path}" {response.status_code} {process_time:.2f}ms'
         )
         return response
-    except Exception as e:
-        process_time = (time.time() - start_time) * 1000
-        logger.error(
-            f'"{request.method} {request.url.path}" 500 {process_time:.2f}ms - Error: {e}',
-            exc_info=True
-        )
-        raise
scripts/deploy.sh (1)

130-133: 인프라 서비스 대기 시간에 매직 넘버 사용 중입니다.

HEALTH_CHECK_INTERVAL처럼 상수로 정의하면 유지보수성이 향상됩니다.

♻️ 상수 정의 제안
 HEALTH_CHECK_RETRIES=30
 HEALTH_CHECK_INTERVAL=5
 DRAIN_WAIT=5
+INFRA_STARTUP_WAIT=5
     log_info "Starting infrastructure services..."
     docker compose -f "$COMPOSE_FILE" up -d postgres redis qdrant
-    sleep 5
+    sleep "$INFRA_STARTUP_WAIT"
src/chats/llm_service.py (1)

45-55: 종료일이 비어있는 경우 표시 방식 고려

end_date가 None이면 프롬프트에 그대로 노출될 수 있습니다. 진행 중 경험이라면 "현재" 등으로 치환하는 방식을 고려해 주세요.

💡 제안 diff
 def _format_experience(exp: Experience) -> str:
     """경험을 프롬프트용 텍스트로 변환"""
-    return f"""### {exp.title}
+    end_date = exp.end_date or "현재"
+    return f"""### {exp.title}
 - 유형: {exp.experience_type}
-- 날짜: {exp.start_date} ~ {exp.end_date}
+- 날짜: {exp.start_date} ~ {end_date}
 - 상황(S): {exp.situation}
src/experience/models.py (1)

43-44: start_dateend_date보다 늦지 않도록 유효성 검사 추가를 권장합니다.

현재 구현에서는 start_dateend_date보다 나중일 경우에도 허용됩니다. Pydantic의 model_validator를 사용하여 날짜 순서를 검증하면 데이터 무결성을 보장할 수 있습니다.

♻️ 날짜 유효성 검사 추가 제안
+from pydantic import model_validator

 class ExperienceBase(BaseModel):
     """Base model for Experience with STAR format fields."""

     title: str = Field(..., min_length=1, max_length=200, description="경험 제목")
     start_date: dt.date = Field(..., description="경험 시작 날짜 (YYYY-MM-DD)")
     end_date: dt.date = Field(..., description="경험 종료 날짜 (YYYY-MM-DD)")
     # ... other fields ...

+    `@model_validator`(mode="after")
+    def validate_dates(self) -> "ExperienceBase":
+        if self.start_date > self.end_date:
+            raise ValueError("start_date는 end_date보다 이전이어야 합니다")
+        return self
src/experience/service.py (2)

514-520: 예외 처리 개선: exception chaining 사용 권장

raise ... from e 패턴을 사용하면 원본 예외의 traceback이 보존되어 디버깅이 용이해집니다.

♻️ 예외 체이닝 추가
     try:
         query_embedding = _generate_embedding(query_text)
-    except Exception as e:
+    except Exception as e:
         raise HTTPException(
             status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
-            detail=f"Failed to generate query embedding: {str(e)}",
-        )
+            detail=f"Failed to generate query embedding: {e!r}",
+        ) from e

534-540: limit=10000 하드코딩에 대한 고려사항

대량의 경험 데이터를 가진 사용자의 경우 성능 이슈가 발생할 수 있습니다. 현재 사용 패턴에서 이 제한이 충분한지 검토가 필요합니다. 추후 페이지네이션이나 설정 가능한 제한값을 고려해 볼 수 있습니다.

Comment on lines +91 to +103
prod-init: ## First-time production setup (starts blue)
chmod +x scripts/deploy.sh
./scripts/deploy.sh init

prod-deploy: ## Blue-green deploy to production
./scripts/deploy.sh deploy

prod-rollback: ## Rollback production to previous version
./scripts/deploy.sh rollback

prod-status: ## Show production deployment status
./scripts/deploy.sh status

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

prod- 타깃에서 deploy.sh 실행 권한 보장 필요*

Line 95-103의 prod-deploy/rollback/status는 실행 권한이 없으면 실패합니다. prod-init만 chmod를 하고 있어 최초 실행 시 문제가 날 수 있습니다.

🛠️ 제안 diff
 prod-deploy: ## Blue-green deploy to production
+	chmod +x scripts/deploy.sh
 	./scripts/deploy.sh deploy

 prod-rollback: ## Rollback production to previous version
+	chmod +x scripts/deploy.sh
 	./scripts/deploy.sh rollback

 prod-status: ## Show production deployment status
+	chmod +x scripts/deploy.sh
 	./scripts/deploy.sh status
🤖 Prompt for AI Agents
In `@Makefile` around lines 91 - 103, The prod-deploy/prod-rollback/prod-status
targets can fail if scripts/deploy.sh is not executable; ensure deploy.sh is
made executable before invoking it by either (A) making those targets depend on
prod-init (e.g., declare prod-deploy, prod-rollback, prod-status depend on
prod-init so prod-init's chmod +x runs first) or (B) adding a chmod +x
scripts/deploy.sh line at the start of each target. Update the Makefile targets
prod-deploy, prod-rollback, and prod-status (and keep prod-init) accordingly so
scripts/deploy.sh is guaranteed executable before ./scripts/deploy.sh is
executed.

Comment on lines +230 to +241
@router.get(
"/match-question/{question_id}",
response_model=ExperienceQuestionMatchResult,
responses=RESPONSES_CRUD_WITH_AUTH,
summary="문항과 매칭되는 경험 조회",
)
async def get_experiences_by_question(
question_id: UUID,
current_user: ActiveUser,
qdrant_client: QdrantDep,
session: SessionDep,
) -> ExperienceQuestionMatchResult:
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

라우트 순서 확인 필요: /match-question/{question_id}/{experience_id} 뒤에 위치

현재 라우트 순서에서 /match-question/{question_id}/{experience_id} 뒤에 정의되어 있습니다. FastAPI는 라우트를 순서대로 매칭하므로, /match-question/... 요청이 /{experience_id} 라우트에 먼저 매칭될 수 있습니다.

/match-question/{question_id} 라우트를 /{experience_id} 라우트보다 앞에 배치하는 것을 권장합니다.

🐛 라우트 순서 수정 제안
 `@router.get`(
     "/search",
     ...
 )
 def search_experiences(...):
     ...

+@router.get(
+    "/match-question/{question_id}",
+    response_model=ExperienceQuestionMatchResult,
+    responses=RESPONSES_CRUD_WITH_AUTH,
+    summary="문항과 매칭되는 경험 조회",
+)
+async def get_experiences_by_question(...):
+    ...

 `@router.get`(
     "/{experience_id}",
     ...
 )
 def get_experience(...):
     ...

-@router.get(
-    "/match-question/{question_id}",
-    ...
-)
-async def get_experiences_by_question(...):
-    ...
🤖 Prompt for AI Agents
In `@src/experience/router.py` around lines 230 - 241, The route definition for
get_experiences_by_question (path "/match-question/{question_id}") can be
shadowed by the generic "/{experience_id}" route because FastAPI matches routes
in order; move the entire get_experiences_by_question function (the
`@router.get`(...) block for "/match-question/{question_id}") so it appears before
the router definition that handles "/{experience_id}" (the generic experience
detail route) to ensure "/match-question/..." is matched first.

…healthy application states during deployment.
@GoGangH GoGangH merged commit e46f20b into main Feb 2, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants