시간정원
← Blog

sideproject · 2026-09-26 · 조회수 3

AI가 매번 .env를 채워 달라고 해서 만든 byulsol secrets

AI와 함께 개발하다 보면 익숙한 요청을 받는다.

.env에 API 키를 설정해 주세요.

프로젝트 하나라면 파일을 열어서 넣으면 된다. 그런데 다른 프로젝트를 만들면 비슷한 일을 다시 한다. 어떤 키가 필요한지 확인하고, 개발용과 운영용을 구분하고, 설정 파일을 어느 폴더에 넣어야 하는지도 챙긴다. 코드는 AI가 작성해도 설정을 준비하는 일은 계속 내 몫이었다.

그래서 프로젝트별 설정을 한곳에 저장하고, AI가 필요한 항목을 준비한 뒤 빌드할 때 가져다 쓰게 만들었다. 이름은 byulsol secrets다. 집의 Ubuntu 서버에 Docker로 올려 두고 웹과 MCP, CLI를 연결했다.

byulsol secrets의 프로젝트별 개발·운영 환경과 AI가 준비한 파일 입력 대기 목록

실제 웹 화면에 공개용 예시 데이터를 넣었다. 왼쪽에서 프로젝트와 환경을 고르면, AI가 준비한 입력 대기 항목을 확인할 수 있다.

키를 보관하는 것에서 한 걸음 더

처음 생각은 단순했다. DB 접속 정보나 API 키를 중앙에 두고, 프로젝트에서 필요할 때 가져오면 되지 않을까?

막상 사용 흐름을 따져 보니 저장 화면만으로는 부족했다. AI가 필요한 변수 이름을 알아야 하고, 내가 아직 입력하지 않은 항목도 구분해야 했다. 값이 준비되면 해당 프로젝트의 빌드 명령까지 연결되어야 했다.

그래서 작업을 세 부분으로 나눴다.

  • AI: 소스를 보고 필요한 설정의 이름, 용도, 환경, 전달 방식을 정한다.
  • 사용자: 웹에서 실제 값을 입력하거나 파일을 업로드한다.
  • CLI: 실행 권한을 확인하고 선택한 설정을 빌드·실행 명령에 전달한다.

AI의 등록 준비, 사용자의 웹 입력, CLI의 설정 전달을 나눈 중앙 설정 관리 흐름

AI는 이름과 준비 상태를 다루고, 실제 값은 웹에서 저장한 뒤 CLI가 가져온다. 동작을 설명하기 위한 개념도다.

예를 들어 새 프로젝트에 외부 API가 필요하면 AI가 PAYMENT_API_KEY라는 항목과 용도를 먼저 등록한다. 나는 안내받은 웹 화면에서 키를 저장한다. 이후 AI는 입력 완료 여부를 확인하고 그 키가 필요한 명령을 실행한다. 키를 대화창에 붙여 넣는 단계는 없다.

MCP와 프로젝트 지침을 함께 둔 이유

AI에게 매번 “우리 중앙 설정 서버를 사용해 줘”라고 설명하면 또 다른 반복 작업이 된다. 공통 개발 지침에 중앙 등록 절차를 남기고, AI가 실제로 호출할 수 있는 MCP 도구도 붙였다.

지침에는 언제 중앙 설정을 사용해야 하는지 적었다. MCP는 프로젝트 목록을 확인하고, 새 프로젝트와 입력 대기 항목을 만들고, 입력 상태와 실행 준비 상태를 조회한다. 각 프로젝트에는 연결된 프로젝트 ID와 실행 방법을 남겨 다음 작업에서도 재사용한다.

둘은 맡은 일이 다르다. 지침이 작업 방향을 알려 준다면, MCP는 지금 무엇이 등록되어 있고 무엇이 빠졌는지 확인할 수 있게 한다.

이 프로젝트의 MCP는 비밀 값을 반환하지 않는다. AI가 받는 것은 변수 이름, 형식, 입력 여부 같은 정보다. 실제 값은 인증된 CLI가 가져와 자식 프로세스에 전달한다. AI가 명령을 구성하는 데 키의 내용까지 알 필요는 없기 때문이다.

다만 이 구조가 실행되는 프로그램의 행동까지 막아 주지는 않는다. 빌드 스크립트가 환경변수를 출력하면 로그에 남을 수 있다. 그래서 필요한 항목만 선택해서 전달하고, 값을 출력하는 명령은 피하도록 했다.

빌드할 때 넣을 값과 서버가 실행 중에 쓸 값

처음에는 중앙에서 값을 가져와 설정 파일을 채우고 빌드하면 끝이라고 생각했다. 실제로 앱에 포함되는 공개 설정은 그렇게 사용할 수 있다. 하지만 서버의 DB 비밀번호처럼 실행 중에 필요한 값도 있다.

byulsol secrets에서는 명령을 실행하는 시점에 설정을 전달하도록 만들었다. 빌드에 필요하면 빌드 명령을, 서버 실행에 필요하면 서버 실행 명령을 CLI로 감싼다. 프로젝트가 .env나 application.properties를 요구한다면 전달받은 환경변수로 파일을 만들거나, 파일 자체를 받아 사용하는 방식으로 연결한다.

등록이 끝났다고 기존 빌드 명령이 저절로 바뀌는 것은 아니다. 프로젝트가 설정을 읽는 위치와 실행 명령을 한 번 연결해야 한다. 이 연결 작업도 AI가 소스를 확인하고 수행하도록 지침에 넣었다.

브라우저에 전달해도 되는 공개 설정과 서버에서만 사용할 비밀 값도 구분한다. 개발 환경에 입력했다고 운영 환경까지 자동으로 열리지는 않는다. 운영 명령에는 별도의 환경 접근 허용과 명시적인 실행 옵션이 필요하다.

환경변수만으로 끝나지 않았다

우리콕 개발을 이어 가다 보니 다음 질문이 생겼다.

google-services.json이나 key.properties도 여기서 관리할 수 없을까?

설정을 옮기는 번거로움은 한 줄짜리 키에만 있는 게 아니었다. Android 프로젝트에는 정해진 위치에 있어야 하는 JSON 파일이 있고, 서명에는 비밀번호와 키스토어 파일이 필요하다. 변수만 중앙에 두면 이런 파일은 여전히 따로 챙겨야 했다.

그래서 웹에서 텍스트 파일과 바이너리 파일을 업로드할 수 있게 확장했다. JSON은 JSON 형식으로, properties 파일은 여러 줄 텍스트로, .jks와 .keystore는 바이너리로 관리한다. 바이너리는 비공개 파일 전달만 허용한다.

byulsol secrets에서 ANDROID_KEYSTORE 항목에 테스트용 파일을 선택한 등록 화면

입력 대기의 ‘값 등록’을 누르면 파일을 선택할 수 있다. 캡처에는 테스트용 파일을 사용했으며 실제 서명 키는 포함하지 않았다.

이 파일들이 모두 같은 성격의 비밀은 아니다. Firebase의 google-services.json에는 앱과 프로젝트를 연결하는 공개 식별 정보가 들어가며, 관리자 서비스 계정의 개인 키와 구분해야 한다. 이 차이는 Firebase의 설정 파일 설명에서도 확인할 수 있다. 중앙에 모으는 이유에는 비밀 보호뿐 아니라 프로젝트별 설정을 빠뜨리지 않고 준비하려는 목적도 있다.

key.properties는 서명 비밀번호와 키스토어 위치를 참조하는 별도 설정이다. 파일을 올리는 것만으로 서명 준비가 끝나지는 않는다. 실제 키스토어와 빌드 설정이 함께 맞아야 한다. 이 관계는 Flutter의 Android 서명 안내를 기준으로 구분했다.

파일 내용은 중앙에, 사용 경로는 프로젝트에

파일을 저장한 다음에는 어디에 놓을지가 남는다. 같은 이름의 설정이라도 프로젝트마다 폴더 구조가 다를 수 있다. 내 PC의 절대 경로를 중앙에 넣으면 다른 작업 환경에서 그대로 쓰기도 어렵다.

그래서 중앙에는 파일 내용을 저장하고, 프로젝트에는 이름과 상대 경로만 적은 매핑 파일을 둔다.

{
  "version": 1,
  "files": [
    {
      "name": "GOOGLE_SERVICES_JSON",
      "path": "android/app/google-services.json"
    }
  ]
}

이 파일은 .byulsol/files.json에 두는 예시다. 비밀 값은 없고, 어떤 설정을 어디에 놓을지만 담는다. 실제 경로는 AI가 프로젝트의 빌드 설정을 확인해서 정한다.

중앙 설정 파일을 프로젝트의 상대 경로에 임시 배치하고 명령 종료 후 정리하는 흐름

경로 매핑과 실제 파일 내용은 따로 관리한다. 기존 파일이 있으면 덮어쓰지 않고 실행을 멈춘다.

실행 명령은 아래 형태다. PROGRAM ARGS는 해당 프로젝트에서 확인한 실제 프로그램과 인수로 바꾼다.

byulsol run --env dev --only GOOGLE_SERVICES_JSON --files .byulsol/files.json -- PROGRAM ARGS

CLI는 선택한 파일을 지정된 위치에 배치하고 명령을 실행한다. 정상 종료뿐 아니라 명령이 실패했을 때도 이번 실행에서 만든 파일을 정리한다. 기존 파일을 덮어쓰거나 프로젝트 밖에 파일을 생성하는 경로는 허용하지 않는다. 대상 파일은 Git 제외 목록에도 넣는다.

서명 설정에 들어가는 키스토어 경로도 같은 기준으로 맞춰야 한다. 한 PC에서만 유효한 절대 경로를 저장하는 대신, 빌드가 읽는 상대 경로나 주입된 경로를 사용한다.

실제로 확인한 범위

현재 중앙 서버는 Ubuntu의 Docker에서 동작하고, 설정을 가져와 명령을 실행하는 CLI는 Windows를 지원한다. 웹에서 JSON·properties·바이너리 파일을 등록하고, CLI가 원래 내용으로 전달한 뒤 정리하는 흐름을 테스트했다. 실제 Java 프로세스가 파일과 properties를 읽는 것도 확인했다.

파일은 하나당 최대 64KiB로 제한했다. 큰 자산을 보관하는 저장소보다는 설정 파일과 키 파일을 관리하는 도구에 가깝다. 프로세스를 강제로 종료하거나 전원이 꺼지면 임시 파일이 남을 수 있으며, 다음 실행에서는 기존 파일 충돌로 멈춘다. 빌드 프로그램이 다른 곳에 복사한 파일까지 자동으로 지워 주지는 않는다.

이번 검증에는 테스트용 파일을 사용했다. 우리콕의 실제 서명 키로 배포하거나 외부 서비스 인증까지 확인한 단계는 아니다. 중앙 등록과 파일 전달이 동작하는 것, 실제 서비스에 연결되고 서명이 유효한 것은 각각 확인해야 한다.

지금은 프로젝트를 만들 때 필요한 설정의 목록과 사용 방법을 AI가 준비하고, 나는 웹에서 값과 파일을 채우는 흐름이 갖춰졌다. 앞으로 새 키가 필요해져도 어느 .env를 열어야 하는지부터 다시 찾기보다, 중앙의 입력 대기를 채운 뒤 같은 실행 경로를 재사용하려고 한다.

댓글

목차