레이블이 python인 게시물을 표시합니다. 모든 게시물 표시
레이블이 python인 게시물을 표시합니다. 모든 게시물 표시

금요일

.env 파일인데 환경변수가 undefined — dotenv 로딩 에러 완전 해결

🔍 검색 키워드: process.env undefined, dotenv not working, .env 파일 적용 안됨, python-dotenv not loading, 환경변수 undefined, docker 환경변수 안됨, next.js env not working

상황

.env 파일을 분명히 만들었는데 코드에서 undefined가 찍힌다.

# .env
DATABASE_URL=postgresql://localhost/mydb
SECRET_KEY=mysecretkey
console.log(process.env.DATABASE_URL); // undefined ???

또는 Python에서:

import os
print(os.getenv("SECRET_KEY"))  # None ???

배포 환경에서만 안 된다거나, Docker 안에서만 안 된다거나, 로컬은 되는데 서버에선 안 된다거나. 패턴은 다양하지만 원인은 몇 가지로 좁혀진다.


Node.js — dotenv 관련

원인 1: dotenv를 아예 안 불렀거나 너무 늦게 불렀다

// 잘못된 예 — DB 모듈보다 나중에 dotenv 로드
const db = require('./database'); // 이미 process.env 읽음
require('dotenv').config();       // 너무 늦었다
// 올바른 예 — 진입점 파일 최상단에서 먼저
require('dotenv').config();

const db = require('./database');
const app = require('./app');

TypeScript / ES Modules:

import 'dotenv/config'; // 이 방법이 제일 깔끔하다

// 또는
import dotenv from 'dotenv';
dotenv.config();

import { createConnection } from './db';

원인 2: .env 파일 경로가 다르다

기본적으로 dotenvprocess.cwd() 기준으로 .env를 찾는다. 실행 위치가 프로젝트 루트가 아니면 못 찾는다.

import path from 'path';
import dotenv from 'dotenv';

dotenv.config({
  path: path.resolve(__dirname, '../.env'),
});

원인 3: .env 파일이 .gitignore에 있고 서버에 없다

로컬에서만 됐던 이유가 이거다. .env는 보통 .gitignore에 들어가 있어서 서버에 배포가 안 된다.

# GitHub Actions — 시크릿 주입
steps:
  - name: Create .env file
    run: |
      echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> .env
      echo "SECRET_KEY=${{ secrets.SECRET_KEY }}" >> .env

원인 4: 변수명에 공백이나 따옴표 문제

# 잘못된 .env
DATABASE_URL = postgresql://localhost/mydb   # = 주변 공백 금지

# 올바른 .env
DATABASE_URL=postgresql://localhost/mydb
SECRET_KEY=mysecret
APP_NAME="My App"  # 값에 공백이 필요하면 따옴표 사용

Python — python-dotenv

from dotenv import load_dotenv
import os

# .env 파일 로드 (현재 디렉토리 기준)
load_dotenv()

# 또는 경로 명시
load_dotenv(dotenv_path='/path/to/.env')

# 이미 설정된 환경변수를 덮어쓰려면
load_dotenv(override=True)

print(os.getenv("DATABASE_URL"))
💡 load_dotenv()는 이미 시스템에 설정된 환경변수는 덮어쓰지 않는다. 테스트 환경에서 .env 값으로 강제하려면 override=True를 써야 한다.
# Django settings.py
import os
from pathlib import Path
from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(BASE_DIR / '.env')

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('DB_NAME'),
        'USER': os.getenv('DB_USER'),
        'PASSWORD': os.getenv('DB_PASSWORD'),
        'HOST': os.getenv('DB_HOST', 'localhost'),
        'PORT': os.getenv('DB_PORT', '5432'),
    }
}

Next.js / React — 프레임워크별 규칙

Next.js는 dotenv를 직접 쓰지 않고 자체 env 로딩 시스템을 쓴다.

  • 서버에서만 쓰는 변수: DATABASE_URL=value
  • 클라이언트(브라우저)에서도 쓰는 변수: NEXT_PUBLIC_API_URL=value (반드시 NEXT_PUBLIC_ 접두사)
// 서버 컴포넌트 / API Route에서만 접근 가능
process.env.DATABASE_URL // ✅

// 클라이언트에서 접근하려면 NEXT_PUBLIC_ 접두사 필수
process.env.NEXT_PUBLIC_API_URL // ✅
process.env.API_URL // 클라이언트에서는 undefined ❌

환경 파일 우선순위:

.env.local > .env.development.local > .env.development > .env
⚠️ NEXT_PUBLIC_ 변수는 빌드 시 정적으로 교체된다. 빌드 후 값을 바꿔도 소용없다. 반드시 빌드 전에 설정해야 한다.

Docker / Docker Compose

# docker-compose.yml
services:
  app:
    build: .
    # 방법 1: env_file 지정 (권장)
    env_file:
      - .env

    # 방법 2: 직접 명시
    environment:
      - DATABASE_URL=postgresql://db:5432/mydb
      - SECRET_KEY=${SECRET_KEY}  # 호스트 환경변수에서
# Docker run
docker run --env-file .env myapp
docker run -e DATABASE_URL=xxx -e SECRET_KEY=yyy myapp
⚠️ Dockerfile에서 COPY .env .를 하면 이미지에 시크릿이 박힌다. .dockerignore.env를 넣고, 런타임에 주입하는 방식을 써야 한다.
# .dockerignore
.env
.env.*

상황별 체크리스트

증상원인확인/해결
로컬만 됨, 서버 안 됨.env가 서버에 없음CI/CD 시크릿 주입 또는 서버에 .env 생성
dotenv.config() 했는데 undefinedimport 순서 문제진입점 최상단에서 dotenv 먼저 로드
특정 변수만 undefined변수명 오타, 공백.env 파일 문법 확인
Docker 안에서 undefinedenv_file 미설정env_file 또는 -e 옵션으로 주입
Next.js 클라이언트에서 undefinedNEXT_PUBLIC_ 접두사 누락접두사 추가 후 재빌드
Python에서 Noneload_dotenv() 미호출import 후 load_dotenv() 호출
기존 시스템 변수가 우선override 미설정load_dotenv(override=True)

디버깅 팁

// 로드된 환경변수 키 목록 확인
console.log('ENV keys:', Object.keys(process.env).filter(k => !k.startsWith('npm_')));

// .env 경로 확인
const path = require('path');
console.log('Looking for .env at:', path.resolve(process.cwd(), '.env'));
# .env 파일에서 읽은 값만 확인 (시스템 변수 제외)
from dotenv import dotenv_values
config = dotenv_values(".env")
print(config)

마무리

환경변수 에러는 대부분 세 가지 중 하나다: .env 파일이 없거나, dotenv 로드를 너무 늦게 했거나, 프레임워크별 규칙을 무시했거나.

서버에 배포했을 때 갑자기 안 된다면 .env가 서버에 실제로 존재하는지부터 확인하고, Docker면 --env-file이나 env_file로 주입됐는지 확인한다. Next.js면 클라이언트에서 쓰는 변수에 NEXT_PUBLIC_ 붙이고 재빌드. 이 세 가지가 90%다.

목요일

Python 메모리 누수 디버깅 완전 가이드

월요일

Python SSL 인증서 에러 완전 정복: CERTIFICATE_VERIFY_FAILED 원인과 해결법

🔍 검색 키워드: CERTIFICATE_VERIFY_FAILED · Python SSL 에러 · ssl.SSLCertVerificationError · Python requests SSL 오류 · macOS Python SSL · Python urllib SSL 인증서 오류

Python으로 외부 API 호출하거나 크롤링하다 보면 이 에러 한 번쯤은 만난다.

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate (_ssl.c:1129)

이거 처음 보면 당황스럽다. 에러 메시지만 봐서는 뭐가 문제인지 감도 안 온다. 원인부터 단계별 해결까지 정리한다.


왜 이 에러가 나는가

Python이 HTTPS 요청을 보낼 때 서버의 SSL 인증서를 검증한다. 이 과정에서 신뢰할 수 있는 루트 인증서(CA) 목록을 참조하는데, 여기에 서버 인증서 체인이 없으면 에러가 난다.

주요 원인은 크게 세 가지다.

  • Python 자체 CA 번들이 오래됨 — macOS에서 Python 3.6+ 공식 배포판은 시스템 인증서를 쓰지 않고 번들로 따로 관리하는데, 이게 업데이트 안 되면 발생한다.
  • 회사 네트워크의 프록시/방화벽 — 기업 환경에서 HTTPS 트래픽을 자체 인증서로 중간에서 가로채는(MITM) 경우, Python이 그 인증서를 모른다.
  • 자체 서명(self-signed) 인증서를 가진 서버 — 내부 개발 서버나 테스트 환경에서 자주 발생.

상황별 체크리스트

상황원인권장 해결책
macOS + Python 공식 설치번들 CA 미업데이트Install Certificates.command 실행
회사 내부 네트워크기업 프록시 인증서기업 CA 인증서 추가
내부 개발 서버self-signed 인증서verify=인증서경로 지정
Docker/CI 환경CA 번들 미포함certifi 패키지 + 환경변수 설정
갑자기 발생 (예전엔 됐는데)Python 또는 OS 업그레이드certifi 재설치

레벨별 해결 방법

초보자 — 일단 돌아가게 만들기 (비추천)

개발 중에 빠르게 확인만 할 때 쓰는 방법이다. 절대 프로덕션에 쓰면 안 된다.

import requests

# SSL 검증 비활성화 — 개발용으로만!
response = requests.get("https://example.com", verify=False)

중급자 — certifi로 CA 번들 업데이트

가장 권장하는 방법이다. certifi는 Mozilla가 관리하는 신뢰할 수 있는 CA 목록을 Python에서 쓸 수 있게 패키징한 라이브러리다.

pip install --upgrade certifi
import requests
import certifi

response = requests.get("https://example.com", verify=certifi.where())
print(response.status_code)

환경변수로 전역 적용 (추천)

# Linux/macOS
export SSL_CERT_FILE=$(python -c "import certifi; print(certifi.where())")
export REQUESTS_CA_BUNDLE=$(python -c "import certifi; print(certifi.where())")

macOS 전용 — Install Certificates.command 실행

# 해당 버전의 Install Certificates 스크립트 실행 (버전에 맞게 경로 수정)
open /Applications/Python\ 3.12/Install\ Certificates.command

고급자 — 기업 CA 인증서 추가

import requests

# 단일 파일
response = requests.get("https://internal.company.com", verify="/path/to/company-ca.crt")
import certifi

with open("/path/to/company-ca.crt", "r") as f:
    company_cert = f.read()

with open(certifi.where(), "a") as bundle:
    bundle.write("\n" + company_cert)

Docker/CI 환경

FROM python:3.12-slim

RUN pip install certifi

ENV SSL_CERT_FILE=/usr/local/lib/python3.12/site-packages/certifi/cacert.pem
ENV REQUESTS_CA_BUNDLE=/usr/local/lib/python3.12/site-packages/certifi/cacert.pem
# GitHub Actions
- name: Fix SSL certificates
  run: |
    pip install --upgrade certifi
    echo "SSL_CERT_FILE=$(python -c 'import certifi; print(certifi.where())')" >> $GITHUB_ENV
    echo "REQUESTS_CA_BUNDLE=$(python -c 'import certifi; print(certifi.where())')" >> $GITHUB_ENV

urllib / aiohttp 사용자 참고

import ssl, urllib.request, certifi

ctx = ssl.create_default_context(cafile=certifi.where())
req = urllib.request.Request("https://example.com")
with urllib.request.urlopen(req, context=ctx) as response:
    data = response.read()
import aiohttp, ssl, certifi

async def fetch(url):
    ssl_ctx = ssl.create_default_context(cafile=certifi.where())
    connector = aiohttp.TCPConnector(ssl=ssl_ctx)
    async with aiohttp.ClientSession(connector=connector) as session:
        async with session.get(url) as response:
            return await response.text()

절대 하면 안 되는 것

⚠️ 프로덕션에서 이런 코드 보이면 반드시 수정해야 한다.
# ❌ 절대 하지 말 것
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

# ❌ 이것도 마찬가지
requests.get(url, verify=False)

# ❌ 환경변수로 전체 비활성화
# PYTHONHTTPSVERIFY=0

SSL 검증을 끄는 순간 중간자 공격(MITM)에 완전히 노출된다. 개발 편의를 위해 껐다가 프로덕션에 그대로 배포되는 사고가 실제로 발생한다.


정리

우선순위해결책상황
1순위Install Certificates.commandmacOS + 공식 Python 설치
2순위pip install --upgrade certifi + 환경변수대부분의 환경
3순위기업 CA 인증서 추가회사 내부 네트워크
4순위verify=False로컬 개발 일시적 확인 (프로덕션 절대 불가)
💡 실무에서 이 에러를 자주 만나는 패턴은 Python 버전 업그레이드 직후, 또는 새 팀원이 회사 환경 세팅할 때다. 팀 위키에 이 내용 정리해두면 반복 질문 많이 줄어든다.

금요일

파이썬 기본 문법 1

https://www.python.org/downloads/

INSTALL *. ADD PYTHON 버전명 TO PATH 체크(고급사용자라면 체크 안해도됨)

ide or idle 선택하여 시작

print(“6+4=”, sum(6,4))

6+4= 10 # 6+4 = 10

x,y=3,4

print(x,y)

3 4

print(y,x)

4 3

name=input()

투이아빠

print(name)

투이아빠

stud_num=input(“학번:”)

학번:20180604

name=input(“이름:”)

이름:투이아빠

print(stud_num, name)

20180604 투이아빠

x=int(input(“정수:”))

정수:100

y=int(input(“정수:”))

정수:200

print(x+y)

300

jetbrains사 pycharm 설치

jetbrains 유료 계정이 있음으로 사이트 내 pycharm을 설치부터 시작

https://www.jetbrains.com/pycharm/?fromMenu