AI가 잘못된 서버에 배포했을 때: 멀티 프로필 관리의 실패 사례
요약
AI 에이전트가 MCP 도구를 사용하여 작업을 수행할 때, CLI의 프로필 설정 우선순위로 인해 스테이징 환경에 잘못 배포된 사례를 분석합니다. 환경 변수와 활성 프로필 간의 우선순위 차이가 의도치 않은 결과를 초래할 수 있음을 경고합니다.
핵심 포인트
- AI 에이전트는 현재 설정된 활성 프로필을 스스로 전환할 수 없음
- 환경 변수(FORMLM_TOKEN)가 프로필 설정보다 우선순위가 높음
- MCP 도구 설계 시 환경 전환(Profile Switching) 기능의 필요성 제기
- 에이전트 사용 시 환경 변수와 활성 프로필 간의 충돌 주의
저에게는 두 개의 FormLM 환경이 있습니다: 스테이징(staging.formlm.me)과 프로덕션(formlm.me)입니다. 저는 새로운 양식(form), 새로운 필드 설정, 그리고 AI 기반 빌드를 테스트하기 위해 스테이징을 사용합니다. 프로덕션은 실제 응답자들이 실제 평가를 작성하는 곳입니다.
formlm-cli는 여러 프로필을 지원하므로, 저는 두 가지를 모두 설정해 두었습니다:
{
"profiles": [
{
...
active 플래그는 CLI가 기본적으로 어떤 프로필을 사용할지 결정합니다. 저는 대부분의 AI 기반 빌드를 스테이징에서 수행하기 때문에 staging을 활성(active) 상태로 유지합니다.
이 설정은 잘 작동했습니다 — 문제가 생기기 전까지는 말이죠.
무슨 일이 일어났나
저는 새로운 직원 참여 설문조사를 테스트하고 있었습니다. 오전 내내 스테이징에서 Claude와 함께 필드 구조 및 점수 산정 방식을 반복하며 설문을 빌드해 왔습니다. 약 20번의 반복 작업 끝에 양식이 완성되었습니다. 저는 Claude에게 다음과 같이 말했습니다:
"이 양식은 준비되었습니다. 이를 게시(publish)하고, 팀원들과 공유할 수 있도록 프로덕션 URL을 알려주세요."
Claude는 share_publish와 share_url을 호출했습니다. 결과로 돌아온 URL은 다음과 같았습니다:
그것은 스테이징 URL이었습니다. 프로덕션이 아니었습니다.
Claude가 스테이징에 게시한 이유는 staging이 활성 프로필이었기 때문입니다. Claude는 프로덕션으로 전환하지 않았습니다. 부분적으로는 제가 그렇게 명령하지 않았기 때문이고, 부분적으로는 MCP 도구들에 "프로필 전환(switch profile)" 작업이 없기 때문입니다.
팀원들에게 링크를 보낸 후, 누군가 "이거 스테이징 환경인가요?"라고 말하기 전까지는 알아채지 못했습니다.
왜 MCP 도구는 프로필을 전환하지 못하는가
설정 코드를 살펴보면, 프로필 관리는 MCP 레벨이 아닌 CLI 레벨에서 처리됩니다:
export function getBaseUrl(): string {
return process.env.FORMLM_BASE_URL || getProfile(runtimeProfile)?.url || getActiveProfile()?.url || 'https://formlm.me';
}
...
여기에는 우선순위 체인이 있습니다:
FORMLM_BASE_URL/FORMLM_TOKEN환경 변수 (가장 높음)runtimeProfile(CLI의--profile글로벌 옵션에 의해 설정됨)activeProfile(config.json에서 활성 상태로 표시된 프로필)- 하드코딩된 기본값 (가장 낮음)
MCP 서버는 프로필 매개변수 없이 getBaseUrl()과 getToken()을 사용합니다. 따라서 항상 활성 프로필 — 또는 환경 변수가 설정된 경우 해당 환경 변수를 사용합니다.
프로필을 전환하는 MCP 도구는 없습니다. config_switch_profile도, config_set_active도 없습니다. AI는 자신이 목표로 하는 환경을 변경할 수 없습니다.
토큰 우선순위 함정 (The Token Priority Trap)
여기서 상황이 더 교묘해집니다. 우선순위 체인은 다음과 같습니다:
- 만약
FORMLM_TOKEN이 환경 변수로 설정되어 있다면, 이는 활성 프로필과 관계없이 모든 것을 덮어씁니다. - 만약
FORMLM_TOKEN이 설정되어 있지 않다면, 다음으로 런타임 프로필을 확인합니다. - 둘 다 설정되어 있지 않다면, 활성 프로필을 사용합니다.
저는 이전 세션에서 셸 환경에 FORMLM_TOKEN을 설정해 두었습니다. 그것은 운영(production) 토큰이었습니다. 하지만 제 활성 프로필은 staging이었습니다. 따라서:
getBaseUrl()은staging.formlm.me를 반환했습니다 (활성 프로필에서 가져옴)getToken()은 운영 토큰을 반환했습니다 (환경 변수에서 가져옴)
저는 스테이징 서버에 운영 토큰을 보내고 있었습니다. 스테이징 서버는 이를 거부했습니다. 오류 메시지는
해결 방법
해결 방법은 세 단계의 수동 프로세스였습니다:
- 셸 환경(shell environment)에서
FORMLM_TOKEN해제 ~/.formlm/config.json을 수동으로 편집하여 올바른 스테이징 토큰(staging token)으로 복구- 게시(publishing)하기 전에 활성 프로필(active profile)을 프로덕션(production)으로 전환
세 번째 단계가 바로 제가 자동화하고 싶은 부분입니다. AI가 프로필을 전환할 수 있게 해주는 MCP 도구가 필요합니다:
server.tool('config_set_active', 'Switch the active profile', {
name: z.string().describe('Profile name'),
}, async (params) => {
...
하지만 아직 추가하지는 않았습니다. 우려는 보안입니다. AI가 프로덕션 환경으로 전환할 수 있어야 할까요? 저의 경우, Claude가 제가 요청하는 대로 수행할 것이라고 믿습니다. 하지만 다른 누군가가 자신의 Claude 인스턴스와 함께 이 CLI를 사용한다면, AI가 프로덕션으로 전환하여 그곳에서 폼(form)을 만들기 시작하는 것을 원치 않을 수도 있습니다.
더 안전한 접근 방식: 명령별 프로필 (Per-Command Profile)
이 CLI는 이미 단일 명령에 대해 런타임 프로필(runtime profile)을 설정하는 --profile 플래그를 지원합니다:
formlm-cli --profile production share publish --app abc123
이 방식은 설정(config)의 활성 프로필을 변경하지 않고, 명령이 실행되는 동안에만 runtimeProfile을 설정합니다. 이는 각 작업에 대해 어떤 환경을 사용할지 명시적으로 지정하므로 가장 안전한 접근 방식입니다.
하지만 MCP 서버는 이를 노출하지 않습니다. 모든 MCP 도구 호출은 프로필 파라미터 없이 execCommand(cmd)를 통해 전달됩니다:
export async function execCommand(cmd: string, profileName?: string): Promise<ExecResult> {
const baseUrl = getBaseUrl();
const token = getToken(profileName);
...
profileName 파라미터는 존재하지만, MCP 도구들에 의해 전달되지 않습니다. 따라서 모든 MCP 도구 호출은 활성 프로필을 사용하게 됩니다.
제가 해야 할 일
제 계획은 다음과 같습니다:
config_list_profiles추가 — AI가 어떤 프로필이 존재하는지(이름과 URL은 포함하되, 토큰은 제외) 확인할 수 있도록 합니다.config_set_active추가 — AI가 프로필을 전환할 수 있게 하되, 기본적으로는 프로덕션 (production) 이외의 프로필 사이에서만 전환하도록 제한합니다.appId범위의 프로필 추가 — 각 앱을 생성할 때 사용된 프로필을 저장하고, AI가 다른 프로필의 앱을 조작하려고 할 때 경고를 보냅니다.
세 번째 방법이 가장 흥미롭습니다. 만약 제가 스테이징 (staging) 환경에서 앱을 생성한다면, 해당 앱 ID는 자신이 스테이징에 속해 있다는 것을 기억해야 합니다. Claude가 프로덕션 프로필을 사용하여 해당 앱 ID를 배포하려고 할 때, 도구는 다음과 같이 경고해야 합니다: "이 앱은 스테이징에서 생성되었습니다. 정말로 프로덕션에 배포하시겠습니까?"
이는 횡단 관심사 (cross-cutting concern)입니다. CLI뿐만 아니라 서버가 어떤 환경에서 각 앱이 생성되었는지 추적해야 하기 때문입니다. 하지만 이 기능이 있었다면 이번 사고 전체를 방지할 수 있었을 것입니다.
교훈: 환경 유출 (Environment Leaking)은 조용히 일어난다
이번 사고에서 가장 최악이었던 점은 에러 자체가 아니라, 바로 _침묵_이었습니다. 제가 프로덕션 토큰을 스테이징으로 보냈을 때, 스테이징 서버는 이를 거부했습니다. 이는 명확합니다. 즉시 에러가 발생하니까요. 하지만 활성 프로필이 스테이징인 상태에서 Claude에게 "프로덕션에 배포해줘"라고 요청했을 때, Claude는 아무런 에러 없이 스테이징에 배포했습니다. 작업은 성공했습니다. URL도 유효했습니다. 단지 잘못된 서버를 가리키고 있었을 뿐입니다.
환경 유출은 잘못된 환경에서 작업이 성공할 때 조용히 일어납니다. 잡아낼 에러가 없습니다. 이를 알아차릴 유일한 방법은 URL을 읽는 것뿐이며, 만약 AI가 세부 사항을 처리할 것이라고 믿고 있다면 확인하지 않을 수도 있습니다.
해결책은 단순히 프로필을 전환하는 도구를 추가하는 것이 아닙니다. 모든 응답에서 환경을 가시화하는 것입니다. Claude가 share_publish를 호출할 때, 응답에는 다음과 같이 환경이 포함되어야 합니다:
✅ staging.formlm.me에 배포되었습니다. URL: https://formlm.me/s/abc123
단순히 URL만 보여주는 것이 아니라, 환경 이름을 명시해야 합니다. 그래야 스테이징 링크를 프로덕션 링크라고 착각하여 실수로 공유하는 일을 방지할 수 있습니다.
저는 아직 이것을 구현하지 않았습니다. 하지만 이번 사건 이후, 이것이 제 할 일 목록의 다음 순위가 되었습니다.
formlm-cli는 프로필별 토큰(token)과 URL을 사용하는 멀티 프로필 관리 (multi-profile management)를 지원합니다. github.com/formlm/cli에서 오픈 소스로 확인하실 수 있으며, formlm.me에서 플랫폼을 체험해 보세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기