Skip to content

환경 변수와 PATH

💡 학습 가이드: 터미널에 git이나 python을 입력할 때마다 시스템은 이 프로그램이 어디 있는지 찾아야 합니다. 코드에서 대형 언어 모델 API를 호출할 때 프로그램은 어떤 키를 사용할지 알아야 합니다. 이 두 가지 일의 배경에는 같은 메커니즘이 있습니다 — 환경 변수.


0. 모든 프로그램 옆에는 설정 묶음이 있습니다

실행 중인 각 프로그램은 '키=값' 형태의 설정 묶음을 가지고 있으며, 이를 환경 변수라고 합니다. 프로그램은 언제든지 이 설정을 읽어 현재 실행 환경을 파악할 수 있습니다.

아래 목록의 아무 변수나 클릭하여 터미널에서 그 값을 "확인"해 보세요:

Environment Variable BrowserClick any variable row to inspect its value and purpose in the terminal
VariableExample value
HOME/Users/alice
USERalice
SHELL/bin/zsh
PATH/usr/local/bin:/usr/bin:/bin
PWD/Users/alice/projects
LANGen_US.UTF-8
NODE_ENVdevelopment
OPENAI_API_KEYsk-••••••••••••••••
bash
← Click any variable on the left to inspect it
$
Core concept:Environment variables are key=value configuration owned by each process. A program inherits a copy from its parent process at startup. You can inspect them with echo $VARIABLE and set them with export KEY=value.

1. PATH: Shell이 입력한 명령어를 찾는 방법

PATH는 특수한 환경 변수로, 콜론으로 구분된 디렉토리 경로 목록을 저장합니다. git을 입력하면 Shell은 이 디렉토리 목록을 순서대로 따라가며 git이라는 실행 파일을 찾습니다 — 첫 번째로 찾으면 즉시 중단합니다.

bash
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

명령어를 선택하고 Shell이 디렉토리를 순서대로 검색하는 과정을 관찰하세요:

PATH Search ProcessEnter a command name and see how the shell searches directories
Choose command:
Current PATH:
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
/usr/local/bin
Waiting
/usr/bin
Waiting
/bin
Waiting
/usr/sbin
Waiting
/sbin
Waiting
Core mechanism:After the shell receives a command name, it searches directories in PATH order. The first match wins and the search stops. That makes PATH order important: earlier directories have higher priority.

세 가지 핵심 규칙:

  • PATH에서 앞에 있는 디렉토리일수록 우선순위가 높습니다
  • 첫 번째로 찾으면 중단하고 계속 검색하지 않습니다
  • 모든 디렉토리에 없으면 → command not found

2. 도구를 설치한 후 왜 터미널을 재시작해야 하나요?

nvm, Homebrew, conda 같은 도구를 설치할 때, 설치 스크립트가 자동으로 ~/.zshrc에 한 줄을 추가하여 자신의 디렉토리를 PATH에 넣습니다:

bash
# 설치 스크립트가 자동으로 작성한 내용 (예시)
export PATH="/usr/local/opt/python@3.12/bin:$PATH"

이 코드는 새 Shell이 시작될 때만 실행됩니다. 이미 열려 있는 터미널 창에는 영향을 주지 않으므로:

bash
# 재시작하지 않고도 바로 적용 가능
source ~/.zshrc

AI 개발 도구에서 자주 발생하는 상황:

bash
# Ollama / pipx 설치 후 command not found 오류
which ollama          # 실제 설치 위치 확인

# pip으로 설치한 CLI 도구 경로 (PATH에 추가)
# macOS: ~/Library/Python/3.x/bin
# Linux: ~/.local/bin
export PATH="$PATH:$HOME/.local/bin"

# 명령줄 도구는 pipx로 설치 권장, PATH 자동 관리
pipx install aider-chat

3. 변수의 유효 범위: 누가 이 변수를 볼 수 있는가?

환경 변수는 모든 프로그램에 방송되는 것이 아닙니다 — 각 프로세스는 부모 프로세스로부터 상속받은 자신만의 사본을 가지고 있으며, 자신의 사본을 수정해도 부모 프로세스에는 영향을 주지 않습니다.

아래 다이어그램은 세 가지 수준을 보여줍니다. '사용자 수준'에서 새로운 변수를 export하고, 그것이 '프로세스 수준'에 나타나는지 확인해 보세요:

Three Levels of Environment VariablesVariables flow one way from outer scopes to inner scopes; child processes inherit a copy from parents
🖥️
System level /etc/environment
Visible to all users and processes; configured by an administrator
PATH=/usr/local/bin:/usr/bin:/bin
LANG=zh_CN.UTF-8
TZ=Asia/Shanghai
▼ Child process inherits the parent environment
👤
User level ~/.zshrc
Affects only the current user and is loaded when the login shell starts
HOME=/Users/alice
SHELL=/bin/zsh
NVM_DIR=$HOME/.nvm
=
▼ Start a child process, such as node app.js
⚙️
Process level (currently running program)
Inherits variables from upper levels, disappears on exit, and does not modify the parent process
PATH=/usr/local/bin:/usr/bin:/bin
LANG=zh_CN.UTF-8
TZ=Asia/Shanghai
HOME=/Users/alice
SHELL=/bin/zsh
NVM_DIR=$HOME/.nvm
NODE_ENV=development
PORT=3000
One-way flow:Variables are inherited downward only. Changing a variable in a child process does not affect its parent. Variables set with export in a terminal also disappear when that terminal closes.

4. export: 자식 프로세스가 이 변수를 읽을 수 있는지 결정

변수를 설정할 때 export를 붙이느냐 안 붙이느냐는 완전히 다른 두 가지 동작입니다:

export Decides Whether Child Processes Can See a VariableToggle the switch and observe whether the child process can read a variable set by the parent
Parent process (Shell)
$MY_VAR="hello"
$echo $MY_VAR
hello
$bash -c 'echo $MY_VAR'
Start child process
Variable not inherited
Child process (bash -c ...)
$echo $MY_VAR
(empty output)
#A child process cannot modify parent variables
Without export: The variable exists only in the current shell, so the child process reads an empty string.

변수가 세션 간에 영구적으로 존재하게 하려면 export를 설정 파일에 작성합니다:

bash
# macOS (zsh)
echo 'export MY_VAR="value"' >> ~/.zshrc
source ~/.zshrc       # 바로 적용, 터미널 재시작 불필요

# Linux (bash)
echo 'export MY_VAR="value"' >> ~/.bashrc
source ~/.bashrc

5. API 키: 절대 코드에 작성하면 안 됩니다

OpenAI, Anthropic, DeepSeek 등의 API를 호출할 때, 키는 여러분의 '신분증 + 신용카드'입니다. 유출되면 다른 사람이 여러분의 할당량을 사용할 수 있고, 비용은 여러분이 부담합니다.

가장 흔한 실수는 키를 코드에 직접 작성하는 것입니다:

Hard-coded Keys vs Environment VariablesThe same feature, two implementations, completely different security outcomes
Dangerous: key written in code
# Python
import openai
 
client = openai.OpenAI(
api_key="sk-proj-abc123..."
)
💀After git push, the key is public on GitHub
💀Crawlers can find it quickly and generate costs
💀GitHub Secret Scanner may revoke the key automatically
💀Deleting the commit is not enough because Git history keeps it
Correct: read from environment variable
# Python
import openai, os
 
client = openai.OpenAI(
api_key=os.environ.get("OPENAI_API_KEY")
)
The code contains no secret and can be open-sourced safely
Development, testing, and production can use different keys
If a key leaks, regenerate it without changing code
Team members can use separate keys without affecting each other
Golden rule:A secret string in code means the secret is already leaked. GitHub Secret Scanner can detect prefixes such as sk- shortly after a push and notify providers to revoke them. Even if you delete the commit, Git history still contains it.

6. 로컬 개발: .env 파일로 키 관리

로컬 개발 시에는 프로젝트 루트 디렉토리의 .env 파일에 키를 저장하고, 코드는 dotenv 라이브러리를 통해 읽어옵니다. .env는 반드시 .gitignore에 추가하여 Git에 커밋하지 않아야 합니다.

왼쪽에 설정을 작성하고, 오른쪽에서 읽어오세요 — 언어를 전환하여 두 가지 작성 방법을 확인하세요:

.env File + Code ReadingConfiguration on the left, code on the right; the variable name is the only link
📄 .env Do not commit
# Local development config, do not commit to Git
OPENAI_API_KEY=sk-proj-abc123...
DATABASE_URL=postgresql://localhost/dev
PORT=3000
NODE_ENV=development
📋 .env.example Can commit
# Copy to .env and fill in real values
OPENAI_API_KEY=(leave empty)
DATABASE_URL=(leave empty)
PORT=(leave empty)
NODE_ENV=(leave empty)
💻 main.py
# pip install python-dotenv openai
from dotenv import load_dotenv
import os, openai
 
load_dotenv() # Read .env file
 
client = openai.OpenAI(
api_key=os.environ.get("OPENAI_API_KEY")
)
 
db = os.environ.get("DATABASE_URL")
port = int(os.environ.get("PORT", 8000))
Values actually read by the program
OPENAI_API_KEYsk-proj-abc123...
DATABASE_URLpostgresql://localhost/dev
PORT3000
Workflow:load_dotenv() / import 'dotenv/config' reads the .env file at startup, injects its key-value pairs into the process environment, and the code reads them with os.environ or process.env. The two sides are connected only by variable names.

7. 프로덕션 환경: 실행 플랫폼이 키를 주입하게 하기

.env는 개발 단계의 편의 도구입니다. 서버와 클라우드 플랫폼에서는 실행 환경이 키를 주입하도록 해야 하며, 코드 자체는 키가 어디에 저장되어 있는지 전혀 인식하지 못해야 합니다:

How Production Injects Secrets.env is a development convenience; servers should not rely on it
/etc/systemd/system/myapp.service
# Recommended: use a separate secret file with controlled permissions
[Service]
EnvironmentFile=/etc/myapp/secrets.env
ExecStart=/usr/bin/node /app/index.js
# Set file permissions so only the owner can read it
sudo chmod 600 /etc/myapp/secrets.env
sudo chown deploy:deploy /etc/myapp/secrets.env
# Reload configuration and restart the service
sudo systemctl daemon-reload
sudo systemctl restart myapp
After chmod 600, only the deploy user can read the secret file; other accounts cannot access it
Secrets are separated from code, so rotating a key does not require redeploying code
Avoid writing Environment="KEY=val" directly in the systemd file; it requires reloads and leaves plaintext in config
Principle:.env files are convenient for local development. In production, the runtime platform should inject environment variables, while application code stays unaware of where secrets live or how they arrived.

8. 실전 문제 해결

command not found

bash
# 첫 번째: PATH에 있는지 확인
which python3         # 출력이 있으면 찾은 것

# 두 번째: 프로그램의 실제 위치 찾기 (macOS)
brew list python | grep bin

# 세 번째: 디렉토리를 PATH에 추가
export PATH="/찾은경로:$PATH"
source ~/.zshrc       # 설정 파일에 기록한 후 source 실행

두 버전이 설치되어 있는데 원하는 버전이 사용되지 않음

bash
which python
# /usr/bin/python ← 시스템 구버전, PATH 앞쪽에 위치

# 새 버전 디렉토리를 PATH 맨 앞으로
export PATH="/usr/local/bin:$PATH"

which python
# /usr/local/bin/python ← 새 버전, 이제 우선순위

변수를 분명히 설정했는데 프로그램이 읽지 못함

원인해결 방법
export를 잊음export 추가 후 재시도
~/.zshrc를 수정했지만 적용 안 됨source ~/.zshrc
.env를 사용하지만 dotenv 미설치pip install python-dotenv / npm install dotenv
서버에서 SSH 세션에서만 유효systemd EnvironmentFile 사용

용어 빠른 참조

용어의미
PATHShell이 실행 파일을 검색하는 디렉토리 목록을 저장, 콜론으로 구분, 순서가 우선순위 결정
export변수를 상속 가능으로 표시, 자식 프로세스 시작 시 자동으로 사본 획득
source현재 Shell에서 설정 파일을 다시 실행하여 수정 사항을 즉시 적용
which특정 명령어에 해당하는 실행 파일 경로 표시 (PATH 검색 결과)
.env프로젝트 로컬 설정 파일, 개발용 키 저장, 반드시 .gitignore에 추가
.env.example변수명은 완전하고 값은 비워둔 템플릿, Git에 안전하게 커밋 가능
chmod 600파일 권한: 소유자만 읽기/쓰기 가능, 키 파일 보호에 적합
Secret ScannerGitHub 등 플랫폼이 키 유출을 자동 스캔, 발견 시 벤더에 통지하여 키 폐기