Skip to content

refactor: Secret Manager SDK 제거하고 env 주입 방식으로 전환 - #51

Merged
sudosubin merged 2 commits into
mainfrom
refactor/secret-env-injection
Aug 8, 2026
Merged

sudosubin merged 2 commits into
mainfrom
refactor/secret-env-injection

Conversation

@sudosubin

@sudosubin sudosubin commented Aug 7, 2026 •

Copy link
Copy Markdown
Collaborator

배경

서버가 뜰 때 @google-cloud/secret-manager를 사용해서 Secret을 로드하고 있었는데, 이로 인해 바이너리 사이즈와 메모리 사용량이 늘어나서 서버가 뜨지 못하고 있습니다.

이를 수정해서, 로컬에서는 별도의 스크립트로 @google-cloud/secret-manager를 사용해 환경 변수를 주입하고, Cloud Run은 인프라 구성으로 외부에서 주입 받도록 수정했습니다. (의존성 제거)

변경

APP_ENV_DOTENV에 담긴 dotenv 원문을 파싱해 process.env를 채웁니다. 주입이 없으면 아무것도 하지 않으므로 로컬 .env만으로도 그대로 시작할 수 있습니다.

주입은 환경별로 이렇게 이뤄집니다.

  • prod: Cloud Run --set-secrets (deploy.yml)
  • local: scripts/with-env.ts가 조회해서 자식 프로세스에 전달

SDK는 이 스크립트에서만 쓰이므로 devDependency로 내렸습니다. 프로덕션 바이너리에는 포함되지 않습니다.

+ 겸사 겸사 테스트 env도 .env.test로 분리했습니다.

효과

🤖 (AI) 프로덕션 바이너리는 99 MB에서 69 MB로 줄었고, 런타임 힙도 함께 감소했습니다. Cloud Run 메모리 한도(128MiB)가 빠듯해서 바이너리 축소는 페이지 캐시 완화에도 도움이 됩니다.

⚠️ 배포 주의

(AI) 이 코드는 APP_ENV_DOTENV 주입을 전제로 합니다. deploy.yml에 --set-secrets가 함께 들어 있어 정상 배포 흐름은 안전하지만, 이미지만 따로 배포하면 env가 비어서 서버 시작에 실패합니다.

Summary by CodeRabbit

  • 개선 사항

    • 로컬 실행 시 필요한 환경 설정을 안전하게 자동 로드하도록 개선했습니다.
    • 운영 환경에서는 배포 과정에서 최신 비밀 설정이 자동으로 주입됩니다.
    • 기존 환경 변수는 우선 유지되어 실행 설정의 안정성이 향상되었습니다.
  • 테스트

    • 테스트 전용 환경 설정을 추가했습니다.
    • 단위 테스트와 E2E 테스트에서 환경 구성이 일관되게 적용됩니다.
  • 문서

    • 로컬 및 운영 환경의 설정 공급 방식 안내를 최신화했습니다.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

로컬 실행 래퍼가 GCP Secret Manager의 dotenv 값을 자식 프로세스에 주입합니다. 애플리케이션은 APP_ENV_DOTENV를 동기적으로 처리합니다. Cloud Run 배포와 테스트 환경이 새 환경 변수 흐름을 사용합니다.

Changes

환경 설정 로딩

Layer / File(s) Summary
애플리케이션 환경 로더
src/config/secret-env.ts, src/main.ts
APP_ENV_DOTENV를 파싱하고 기존 환경 변수가 없는 키만 주입합니다. 처리 후 원문 변수를 삭제하며, 호출은 동기 방식으로 변경되었습니다.
로컬 및 배포 환경 주입
scripts/with-env.ts, package.json, .github/workflows/deploy.yml, .env.example, infra/src/resources/secrets.ts
로컬 래퍼가 Secret Manager 값을 조회하고 자식 명령을 실행합니다. Cloud Run은 team-mino-env-prod:latest를 APP_ENV_DOTENV로 주입합니다. 개발자 서비스 계정에는 시크릿 조회와 버전 갱신 권한이 추가되었습니다.
테스트 환경 및 데이터베이스 초기화
.env.test, .gitignore, e2e/setup.ts, scripts/db-schema-init.ts
테스트용 환경 변수를 추가하고 .env.test를 무시합니다. E2E 데이터베이스 설정은 DATABASE_SCHEMA를 사용합니다. 환경 로딩 호출에서는 비동기 대기를 제거했습니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: minsour, kkardy, labyrinth30

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 Secret Manager SDK 제거와 환경 변수 주입 방식 전환이라는 핵심 변경을 간결하게 설명합니다.
Description check ✅ Passed 변경 배경, 구현 방식, 효과, 배포 주의사항을 구체적으로 설명해 템플릿의 핵심 정보를 대부분 충족합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/secret-env-injection

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.

@github-actions

github-actions Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

🍹 preview on team-mino-infra/prod

Pulumi report
   Previewing update (prod):
Downloading plugin gcp-9.25.0: starting
Downloading plugin gcp-9.25.0: done
Installing plugin gcp-9.25.0: starting
Installing plugin gcp-9.25.0: done

~  pulumi:pulumi:Stack team-mino-infra-prod refreshing 
~  gcp:projects:Service artifactregistry.googleapis.com refreshing 
~  gcp:projects:Service secretmanager.googleapis.com refreshing 
~  pulumi:pulumi:Stack team-mino-infra-prod refreshing 
~  gcp:projects:Service run.googleapis.com refreshing 
~  gcp:projects:IAMMember developers-group-run-developer refreshing 
~  gcp:projects:IAMMember developers-group-cloudtasks-viewer refreshing 
~  gcp:projects:Service cloudtasks.googleapis.com refreshing 
~  gcp:projects:IAMMember developers-group-viewer refreshing 
~  gcp:projects:Service aiplatform.googleapis.com refreshing 
~  gcp:projects:IAMMember developers-group-vertex-user refreshing 
~  gcp:projects:Service firebase.googleapis.com refreshing 
~  gcp:projects:Service firebasehosting.googleapis.com refreshing 
~  gcp:projects:IAMMember developers-group-artifactregistry-writer refreshing 
~  gcp:projects:Service storage.googleapis.com refreshing 
~  gcp:projects:Service cloudscheduler.googleapis.com refreshing 
~  gcp:projects:IAMMember developers-group-logging-viewer refreshing 
~  gcp:projects:IAMMember developers-group-secret-accessor refreshing 
~  gcp:projects:IAMMember developers-group-artifactregistry-writer refresh 
~  gcp:serviceaccount:IAMMember developers-group-impersonate-infra refreshing 
~  gcp:serviceaccount:IAMMember developers-group-impersonate-infra refresh 
~  gcp:serviceaccount:Account team-mino-prod-developer refreshing 
~  gcp:projects:IAMMember developers-group-secret-accessor refresh 
~  gcp:serviceaccount:Account team-mino-prod-server refreshing 
~  gcp:serviceaccount:Account team-mino-prod-developer refresh 
~  gcp:serviceaccount:Account team-mino-prod-tasks-invoker refreshing 
~  gcp:serviceaccount:Account team-mino-prod-server refresh 
~  gcp:firebase:Project team-mino refreshing 
~  gcp:projects:IAMMember developers-group-viewer refresh 
~  gcp:secretmanager:Secret team-mino-env-prod refreshing 
~  gcp:serviceaccount:Account team-mino-prod-tasks-invoker refresh 
~  gcp:projects:ServiceIdentity team-mino-aiplatform-agent refreshing 
~  gcp:projects:ServiceIdentity team-mino-aiplatform-agent refresh 
~  gcp:iam:WorkloadIdentityPool team-mino-prod-github refreshing 
~  gcp:secretmanager:Secret team-mino-env-prod refresh 
~  gcp:secretmanager:Secret team-mino-env-local refreshing 
~  gcp:projects:IAMMember developers-group-cloudtasks-viewer refresh 
~  gcp:storage:Bucket team-mino-place-images-local refreshing 
~  gcp:secretmanager:Secret team-mino-env-local refresh 
~  gcp:artifactregistry:Repository team-mino-prod-docker refreshing 
~  gcp:firebase:Project team-mino refresh 
~  gcp:cloudtasks:Queue team-mino-prod-place-extraction refreshing 
~  gcp:iam:WorkloadIdentityPool team-mino-prod-github refresh 
~  gcp:storage:Bucket team-mino-place-images-prod refreshing 
~  gcp:projects:IAMMember developers-group-vertex-user refresh 
~  gcp:serviceaccount:IAMMember developers-group-impersonate-developer refreshing 
~  gcp:storage:Bucket team-mino-place-images-local refresh 
~  gcp:projects:IAMMember developer-logging-viewer refreshing 
~  gcp:artifactregistry:Repository team-mino-prod-docker refresh 
~  gcp:projects:IAMMember developer-artifactregistry-writer refreshing 
~  gcp:serviceaccount:IAMMember developers-group-impersonate-developer refresh 
~  gcp:projects:IAMMember developer-run-developer refreshing 
~  gcp:projects:IAMMember developers-group-run-developer refresh 
~  gcp:projects:IAMMember developer-cloudtasks-viewer refreshing 
~  gcp:cloudtasks:Queue team-mino-prod-place-extraction refresh 
~  gcp:serviceaccount:IAMMember team-mino-prod-developer-act-as-runtime refreshing 
~  gcp:serviceaccount:IAMMember team-mino-prod-developer-act-as-runtime refresh 
~  gcp:projects:IAMMember team-mino-prod-server-vertex-user refreshing 
~  gcp:projects:IAMMember developers-group-logging-viewer refresh 
~  gcp:projects:IAMMember team-mino-prod-server-log-writer refreshing 
~  gcp:projects:IAMMember developer-logging-viewer refresh 
~  gcp:cloudrunv2:Service team-mino-prod-api refreshing 
~  gcp:storage:Bucket team-mino-place-images-prod refresh 
~  gcp:serviceaccount:IAMMember team-mino-prod-tasks-invoker-actas refreshing 
~  gcp:serviceaccount:IAMMember team-mino-prod-tasks-invoker-actas refresh 
~  gcp:serviceaccount:IAMMember team-mino-prod-developer-impersonate-tasks-invoker refreshing 
~  gcp:projects:IAMMember developer-artifactregistry-writer refresh 
~  gcp:firebase:HostingSite team-mino-prod refreshing 
~  gcp:serviceaccount:IAMMember team-mino-prod-developer-impersonate-tasks-invoker refresh 
~  gcp:secretmanager:SecretIamMember team-mino-env-prod-runtime refreshing 
~  gcp:projects:IAMMember developer-run-developer refresh 
~  gcp:iam:WorkloadIdentityPoolProvider team-mino-prod-github refreshing 
~  gcp:iam:WorkloadIdentityPoolProvider team-mino-prod-github refresh 
~  gcp:serviceaccount:IAMMember team-mino-prod-ci-workload-identity-user refreshing 
~  gcp:secretmanager:SecretIamMember team-mino-env-prod-runtime refresh 
~  gcp:storage:BucketIAMMember team-mino-place-images-local-developers refreshing 
~  gcp:projects:IAMMember developer-cloudtasks-viewer refresh 
~  gcp:storage:BucketIAMMember team-mino-place-images-local-vertex-reader refreshing 
~  gcp:serviceaccount:IAMMember team-mino-prod-ci-workload-identity-user refresh 
~  gcp:cloudtasks:QueueIamMember team-mino-prod-place-extraction-developer-enqueuer refreshing 
~  gcp:projects:IAMMember team-mino-prod-server-vertex-user refresh 
~  gcp:cloudtasks:QueueIamMember team-mino-prod-place-extraction-enqueuer refreshing 
~  gcp:firebase:HostingSite team-mino-prod refresh 
~  gcp:storage:BucketIAMMember team-mino-place-images-prod-server-writer refreshing 
~  gcp:storage:BucketIAMMember team-mino-place-images-local-developers refresh 
~  gcp:storage:BucketIAMMember team-mino-place-images-prod-vertex-reader refreshing 
~  gcp:cloudrunv2:Service team-mino-prod-api refresh 
~  gcp:cloudrunv2:ServiceIamMember team-mino-prod-api-public-invoker refreshing 
~  gcp:projects:IAMMember team-mino-prod-server-log-writer refresh 
~  gcp:cloudrunv2:ServiceIamMember team-mino-prod-tasks-invoker-run-invoker refreshing 
~  gcp:cloudrunv2:ServiceIamMember team-mino-prod-api-public-invoker refresh 
~  gcp:cloudscheduler:Job team-mino-prod-health-ping refreshing 
~  gcp:storage:BucketIAMMember team-mino-place-images-local-vertex-reader refresh 
~  gcp:firebase:HostingVersion api refreshing 
~  gcp:cloudtasks:QueueIamMember team-mino-prod-place-extraction-developer-enqueuer refresh 
~  gcp:firebase:HostingCustomDomain api-gguk-org refreshing 
~  gcp:firebase:HostingVersion api refresh 
~  gcp:firebase:HostingRelease api refreshing 
~  gcp:storage:BucketIAMMember team-mino-place-images-prod-server-writer refresh 
~  gcp:firebase:HostingCustomDomain api-gguk-org refresh 
~  gcp:storage:BucketIAMMember team-mino-place-images-prod-vertex-reader refresh 
~  gcp:cloudtasks:QueueIamMember team-mino-prod-place-extraction-enqueuer refresh 
~  gcp:projects:Service secretmanager.googleapis.com refresh 
~  gcp:projects:Service firebase.googleapis.com refresh 
~  gcp:projects:Service cloudtasks.googleapis.com refresh 
~  gcp:projects:Service cloudscheduler.googleapis.com refresh 
~  gcp:projects:Service firebasehosting.googleapis.com refresh 
~  gcp:projects:Service storage.googleapis.com refresh 
~  gcp:projects:Service run.googleapis.com refresh 
~  gcp:projects:Service aiplatform.googleapis.com refresh 
~  gcp:projects:Service artifactregistry.googleapis.com refresh 
~  gcp:firebase:HostingRelease api refresh 
~  gcp:cloudscheduler:Job team-mino-prod-health-ping refresh 
~  gcp:cloudrunv2:ServiceIamMember team-mino-prod-tasks-invoker-run-invoker refresh 
   pulumi:pulumi:Stack team-mino-infra-prod running 
   pulumi:providers:gcp default_9_25_0  warning: failed to get regions list: failed to list regions: googleapi: Error 403: Compute Engine API has not been used in project team-mino-prod before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=team-mino-prod then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
   gcp:projects:Service run.googleapis.com  
   gcp:projects:Service artifactregistry.googleapis.com  
   gcp:projects:Service cloudtasks.googleapis.com  
   gcp:projects:Service aiplatform.googleapis.com  
   gcp:projects:Service secretmanager.googleapis.com  
   gcp:projects:Service cloudscheduler.googleapis.com  
   gcp:projects:Service firebasehosting.googleapis.com  
   gcp:projects:Service storage.googleapis.com  
   gcp:projects:Service firebase.googleapis.com  
   gcp:projects:IAMMember developers-group-run-developer  
   gcp:projects:IAMMember developers-group-cloudtasks-viewer  
   gcp:projects:IAMMember developers-group-vertex-user  
   gcp:projects:IAMMember developers-group-artifactregistry-writer  
   gcp:projects:IAMMember developers-group-logging-viewer  
   gcp:projects:IAMMember developers-group-viewer  
   gcp:projects:IAMMember developers-group-secret-accessor  
   gcp:serviceaccount:Account team-mino-prod-server  
   gcp:serviceaccount:IAMMember developers-group-impersonate-infra  
   gcp:serviceaccount:Account team-mino-prod-tasks-invoker  
   gcp:serviceaccount:Account team-mino-prod-developer  
   gcp:firebase:Project team-mino  
   gcp:secretmanager:Secret team-mino-env-local  
   gcp:secretmanager:Secret team-mino-env-prod  
   gcp:projects:ServiceIdentity team-mino-aiplatform-agent  
   gcp:iam:WorkloadIdentityPool team-mino-prod-github  
   gcp:artifactregistry:Repository team-mino-prod-docker  
   gcp:storage:Bucket team-mino-place-images-prod  
   gcp:cloudtasks:Queue team-mino-prod-place-extraction  
   gcp:storage:Bucket team-mino-place-images-local  
   gcp:cloudrunv2:Service team-mino-prod-api  
   gcp:projects:IAMMember team-mino-prod-server-vertex-user  
   gcp:projects:IAMMember team-mino-prod-server-log-writer  
   gcp:serviceaccount:IAMMember team-mino-prod-tasks-invoker-actas  
   gcp:serviceaccount:IAMMember developers-group-impersonate-developer  
   gcp:projects:IAMMember developer-logging-viewer  
   gcp:projects:IAMMember developer-artifactregistry-writer  
   gcp:projects:IAMMember developer-run-developer  
   gcp:projects:IAMMember developer-cloudtasks-viewer  
   gcp:serviceaccount:IAMMember team-mino-prod-developer-impersonate-tasks-invoker  
   gcp:serviceaccount:IAMMember team-mino-prod-developer-act-as-runtime  
   gcp:firebase:HostingSite team-mino-prod  
+  gcp:secretmanager:SecretIamMember team-mino-env-local-developer-read create 
+  gcp:secretmanager:SecretIamMember team-mino-env-local-developer-write create 
   gcp:secretmanager:SecretIamMember team-mino-env-prod-runtime  
+  gcp:secretmanager:SecretIamMember team-mino-env-prod-developer-read create 
+  gcp:secretmanager:SecretIamMember team-mino-env-prod-developer-write create 
   gcp:iam:WorkloadIdentityPoolProvider team-mino-prod-github  
   gcp:serviceaccount:IAMMember team-mino-prod-ci-workload-identity-user  
   gcp:storage:BucketIAMMember team-mino-place-images-prod-server-writer  
   gcp:storage:BucketIAMMember team-mino-place-images-prod-vertex-reader  
   gcp:cloudtasks:QueueIamMember team-mino-prod-place-extraction-enqueuer  
   gcp:cloudtasks:QueueIamMember team-mino-prod-place-extraction-developer-enqueuer  
   gcp:storage:BucketIAMMember team-mino-place-images-local-developers  
   gcp:storage:BucketIAMMember team-mino-place-images-local-vertex-reader  
   gcp:cloudrunv2:ServiceIamMember team-mino-prod-api-public-invoker  
   gcp:cloudrunv2:ServiceIamMember team-mino-prod-tasks-invoker-run-invoker  
   gcp:cloudscheduler:Job team-mino-prod-health-ping  
   gcp:firebase:HostingVersion api  
   gcp:firebase:HostingCustomDomain api-gguk-org  
   gcp:firebase:HostingRelease api  
   pulumi:providers:gcp default_9_25_0  1 warning
   pulumi:pulumi:Stack team-mino-infra-prod  
Diagnostics:
 pulumi:providers:gcp (default_9_25_0):
   warning: failed to get regions list: failed to list regions: googleapi: Error 403: Compute Engine API has not been used in project team-mino-prod before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=team-mino-prod then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
   Details:
   [
     {
       "@type": "type.googleapis.com/google.rpc.ErrorInfo",
       "domain": "googleapis.com",
       "metadata": {
         "activationUrl": "https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=team-mino-prod",
         "consumer": "projects/team-mino-prod",
         "containerInfo": "team-mino-prod",
         "service": "compute.googleapis.com",
         "serviceTitle": "Compute Engine API"
       },
       "reason": "SERVICE_DISABLED"
     },
     {
       "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
       "locale": "en-US",
       "message": "Compute Engine API has not been used in project team-mino-prod before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=team-mino-prod then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."
     },
     {
       "@type": "type.googleapis.com/google.rpc.Help",
       "links": [
         {
           "description": "Google developers console API activation",
           "url": "https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=team-mino-prod"
         }
       ]
     }
   ]
   , accessNotConfigured

Resources:
   + 4 to create
   57 unchanged

   

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with 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.

Inline comments:
In `@scripts/with-env.ts`:
- Around line 41-61: Update scripts/with-env.ts lines 41-61 so local execution
can fall back to the existing local environment when fetchSecret fails, or make
local file mode the default while preserving Secret Manager behavior for
explicitly configured environments. Update .env.example lines 1-6 to include the
required APP_CONFIG_SOURCE=env setting and accurately document the local startup
procedure.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 96a4f825-f867-4737-b573-fd843937f440

📥 Commits

Reviewing files that changed from the base of the PR and between f670bc8 and 4193304.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock, !bun.lock
📒 Files selected for processing (10)
  • .env.example
  • .env.test
  • .github/workflows/deploy.yml
  • .gitignore
  • e2e/setup.ts
  • package.json
  • scripts/db-schema-init.ts
  • scripts/with-env.ts
  • src/config/secret-env.ts
  • src/main.ts

Comment thread scripts/with-env.ts
Comment on lines +41 to +61
if (env.APP_CONFIG_SOURCE !== "env") {
const project = env.GCP_PROJECT ?? "team-mino-prod";
const secret = env.GCP_ENV_SECRET ?? `team-mino-env-${env.APP_ENV}`;

let payload: string;
try {
payload = await fetchSecret(project, secret);
} catch (error) {
console.error(`시크릿을 가져오지 못했습니다: ${secret} (${project})`);
console.error(error instanceof Error ? error.message : error);
console.error(
"ADC 로그인(gcloud auth application-default login)을 확인하거나, APP_CONFIG_SOURCE=env 로 우회하세요.",
);
process.exit(1);
}

env[ENV_DOTENV_VAR] = payload;
for (const [key, value] of Object.entries(parseDotenv(payload))) {
if (env[key] === undefined) env[key] = value;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

로컬 .env 대체 경로를 실제로 동작하게 해야 합니다.

APP_ENV는 Line 39에서 기본값 local을 사용합니다. 그러나 APP_CONFIG_SOURCE=env가 없으면 Line 41이 Secret Manager 조회를 시작합니다. ADC가 없으면 Line 48-54가 종료하므로, 기존 로컬 .env만으로는 start:local을 실행할 수 없습니다.

  • scripts/with-env.ts#L41-L61: 로컬 Secret Manager 조회가 실패하면 기존 로컬 환경으로 자식 명령을 실행하거나, 로컬 파일 모드를 명시적으로 기본 선택해야 합니다.
  • .env.example#L1-L6: 로컬 파일 모드가 APP_CONFIG_SOURCE=env를 요구한다면 그 값을 포함하고 실행 절차를 정확히 문서화해야 합니다.
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 48-48: Avoid logging sensitive data
Context: console.error(시크릿을 가져오지 못했습니다: ${secret} (${project}))
Note: [CWE-532] Insertion of Sensitive Information into Log File.

(log-sensitive-data-typescript)

📍 Affects 2 files
  • scripts/with-env.ts#L41-L61 (this comment)
  • .env.example#L1-L6
🤖 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 `@scripts/with-env.ts` around lines 41 - 61, Update scripts/with-env.ts lines
41-61 so local execution can fall back to the existing local environment when
fetchSecret fails, or make local file mode the default while preserving Secret
Manager behavior for explicitly configured environments. Update .env.example
lines 1-6 to include the required APP_CONFIG_SOURCE=env setting and accurately
document the local startup procedure.

@sudosubin sudosubin self-assigned this Aug 7, 2026

@labyrinth30 labyrinth30 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.

전혀 예상치 못했던 상황인데 선조치해주셔서 감사합니다~
처음엔 SM을 안 쓰는 건가 했는데 서버 입장에서 환경변수를 언제, 어떻게 알 수 있는지를 고친 것이구나라고 이해했어요.
고생했고, 현재는 멀티 라인 환경 변수가 없어서 approve를 했는데, 리뷰에 단 것처럼 멀티 라인 환경 변수는 어떻게 하면 좋을지 간단하게 이야기 해보죠!

Comment thread src/config/secret-env.ts
*
* 부팅 최초 단계(ConfigModule 검증 이전)라 ConfigService가 없으므로,
* "env는 ConfigService로만" 규칙의 의도적 예외로 process.env를 직접 다룹니다.
* 시크릿 포맷은 한 줄 = 한 키(여러 줄 값은 base64로 인코딩).

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.

'여러 줄 값은 base64로 인코딩' 이라고 되어있는데 parseDotenv 를 봤을 때 어떤 환경변수가 base64로 인코딩된 것인지 알 수 없겠다는 생각이 들었어요.

나중에 여러 줄짜리 환경변수가 생겼을 때는 어떻게 하면 좋을까요? 👀

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

아직 별 고민이 없긴 했는데, parseDotenv에서의 처리는 아니고 사용처에서 여러 줄짜리 환경 변수면 decode 해서 쓰면 되지 않을까 했어요. 구체적인 사용 사례를 떠올려보면 pem key 같은 것이긴 합니다. (그 key를 사용하는 쪽에서 그 key만 따로 encode 해서 Secrets Manager에 넣고, 사용할 때에는 decode 해서 사용)

@KKardy KKardy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

확인했습니다 🍀 🚀
다만, 조금 우려가 되는점은 배포가 된 뒤에도 128Mi 로 견뎌줄지가 고민이네요.
지금 상황에서 가장 메모리를 많이 쓰는 부분은 이미지 다운로드하고 GCS에 업로드 하는거라고 생각드는데요..!
그 작업이 진행됐을 때, 얼마나 메모리를 잡아먹는지 테스트해봐야할 거 같습니다.

아니면 CloudRun의 오토 스케일업 정책같은게 있을까요? 👀

@sudosubin
sudosubin merged commit 6372237 into main Aug 8, 2026
6 of 7 checks passed
@sudosubin
sudosubin deleted the refactor/secret-env-injection branch August 8, 2026 06:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with 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.

Inline comments:
In `@infra/src/resources/secrets.ts`:
- Around line 56-67: Update the production secret IAM bindings for prodEnvSecret
so developerMember retains only the required read access and no longer receives
roles/secretmanager.secretVersionManager. Grant production
write/version-management access exclusively to the existing dedicated release or
rotation service-account member, while keeping developerMember’s local-secret
access unchanged and ensuring deployments still succeed without developer
permissions.
- Around line 42-55: Align local ADC authentication with the developer service
account used by fetchSecret: configure local setup to obtain ADC with
impersonation of developer.email, or grant the approved local user/group the
minimum secretAccessor permission on localEnvSecret. Ensure the resulting ADC
principal matches an authorized member while preserving the existing developer
write permission.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6d9113f8-aca1-4951-a565-64c06011c830

📥 Commits

Reviewing files that changed from the base of the PR and between 4193304 and 6039c01.

📒 Files selected for processing (1)
  • infra/src/resources/secrets.ts

Comment on lines +42 to +55
const developerMember = pulumi.interpolate`serviceAccount:${developer.email}`;

new gcp.secretmanager.SecretIamMember("team-mino-env-local-developer-read", {
secretId: localEnvSecret.secretId,
project,
role: "roles/secretmanager.secretAccessor",
member: developerMember,
});
new gcp.secretmanager.SecretIamMember("team-mino-env-local-developer-write", {
secretId: localEnvSecret.secretId,
project,
role: "roles/secretmanager.secretVersionManager",
member: developerMember,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 6 \
  'gcloud auth application-default login|GOOGLE_APPLICATION_CREDENTIALS|impersonat|fetchSecret|developer\.email|serviceAccount:' \
  --glob '*.ts' --glob '*.yml' --glob '*.yaml' .

Repository: mash-up-kr/Team-MINO-Node

Length of output: 13948


🌐 Web query:

Google Cloud SDK gcloud auth application-default login User credentials Service Account impersonation

💡 Result:

To use service account impersonation with Application Default Credentials (ADC) in a local development environment, you can use the gcloud auth application-default login command with the --impersonate-service-account flag [1][2][3]. This command generates a local ADC file that allows client libraries to authenticate using the impersonated service account's identity [1][2][4]. Usage: gcloud auth application-default login --impersonate-service-account=SERVICE_ACCT_EMAIL [1][3] Key requirements and considerations: - Permissions: The user account executing the command must have the Service Account Token Creator (roles/iam.serviceAccountTokenCreator) IAM role on the target service account [2][3]. This role provides the required iam.serviceAccounts.getAccessToken permission [3]. - Language Support: While ADC impersonation works for many use cases, support for credentials generated this way is not universal across all Google Cloud client libraries [2]. It is officially supported for Go, Java, Node.js, and Python client libraries [3]. - Purpose: This approach is intended for local development environments where you need your code to test permissions assigned to a specific service account without using long-lived service account keys [2][5][3]. - Distinction from gcloud CLI Config: Note that there is a difference between using this flag with gcloud auth application-default login (which creates an ADC file for client libraries) and setting the gcloud config property auth/impersonate_service_account [1][3]. The latter configures the gcloud CLI tool itself to impersonate a service account for its own commands, but it does not necessarily create the ADC file used by your application code [1][6].

Citations:


로컬 ADC가 developer 서비스 계정과 같은 주체가 되도록 맞춰 주세요.

gcloud auth application-default login만 실행하면 로그인한 사용자 계정의 ADC가 생성되며, 현재 Secret Manager의 roles/secretmanager.secretAccessor 권한은 serviceAccount:${developer.email}에만 있습니다. 이런 상태에서는 fetchSecret이 호출 주체와 권한이 달라 PERMISSION_DENIED에 실패할 수 있습니다. 로컬 실행 시 --impersonate-service-account=${developer.email} ADC를 사용하거나, 승인된 사용자/그룹에 최소 권한을 부여해 주체를 일치시켜 주세요.

🤖 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 `@infra/src/resources/secrets.ts` around lines 42 - 55, Align local ADC
authentication with the developer service account used by fetchSecret: configure
local setup to obtain ADC with impersonation of developer.email, or grant the
approved local user/group the minimum secretAccessor permission on
localEnvSecret. Ensure the resulting ADC principal matches an authorized member
while preserving the existing developer write permission.

Comment on lines +56 to +67
new gcp.secretmanager.SecretIamMember("team-mino-env-prod-developer-read", {
secretId: prodEnvSecret.secretId,
project,
role: "roles/secretmanager.secretAccessor",
member: developerMember,
});
new gcp.secretmanager.SecretIamMember("team-mino-env-prod-developer-write", {
secretId: prodEnvSecret.secretId,
project,
role: "roles/secretmanager.secretVersionManager",
member: developerMember,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n -C 8 \
  'team-mino-env-prod|secretVersionManager|addSecretVersion|accessSecretVersion|gcloud secrets' \
  --glob '*.ts' --glob '*.yml' --glob '*.yaml' .

Repository: mash-up-kr/Team-MINO-Node

Length of output: 5851


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,200p'

echo "== secrets/identity config =="
for f in infra/src/resources/secrets.ts infra/src/resources/identity.ts infra/src/config.ts scripts/with-env.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ($(wc -l < "$f") lines) ---"
    cat -n "$f"
  fi
done

echo "== broad secret/cloud run references =="
rg -n -C 4 \
  'with-env|with-env\.ts|envDotenv|ENV_DOTENV_VAR|secretEnv|secrets\.env|DATABASE_URL|secretManager|SecretIamMember|secretVersionManager|secretAccessor|gcloud secrets' .

Repository: mash-up-kr/Team-MINO-Node

Length of output: 27991


프로덕션 시크릿 쓰기 권한을 개발자 서비스 계정에서 분리하세요.

infra/src/resources/secrets.ts:56-67에서 developerMember가 team-mino-env-prod의 roles/secretmanager.secretAccessor와 roles/secretmanager.secretVersionManager를 모두 갖습니다. 이 권한으로 개발자가 프로덕션 시크릿을 읽고 버전을 추가·관리할 수 있습니다.

로컬 실행에는 team-mino-env-local의 접근 권한만 사용하세요. 프로덕션 값 갱신이 필요하면 dedicated release/rotation 서비스 계정을 부여하고, 개발자 권한이 없으면 배포가 실패하지 않는지 확인하세요.

🤖 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 `@infra/src/resources/secrets.ts` around lines 56 - 67, Update the production
secret IAM bindings for prodEnvSecret so developerMember retains only the
required read access and no longer receives
roles/secretmanager.secretVersionManager. Grant production
write/version-management access exclusively to the existing dedicated release or
rotation service-account member, while keeping developerMember’s local-secret
access unchanged and ensuring deployments still succeed without developer
permissions.

This branch was previously deployed

1 inactive deployment
team-mino-prod — 6039c01a Deployed Aug 8, 2026 by sudosubin via infra-preview #205
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.

3 participants