본문 바로가기

개발 & IT/백엔드

Laravel 운영 서버에서 env() 함수가 null을 반환하는 이유

문제 상황

Laravel 프로젝트를 운영 서버에 배포한 후, 이상한 현상을 경험했습니다.

// Blade 파일
<script>
const config = {
    fileServerHost: "{{ env('FILESERVER_HOST') }}"
};
</script>

분명 .env 파일에 FILESERVER_HOST 값이 설정되어 있는데, 화면에서는 빈 문자열이 출력되었습니다. 더 이상한 건 DB 연결은 정상적으로 작동한다는 점이었죠.
tinker로 확인해보니:

$ php artisan tinker
> env('FILESERVER_HOST')
= null
> env('DB_HOST')
= null
> env('APP_NAME')
= null

모든 환경 변수가 null로 반환되었습니다. 하지만 애플리케이션은 정상적으로 데이터베이스에 접속하고 있었습니다. 도대체 무슨 일이 벌어진 걸까요?

원인: config:cache의 동작 원리

문제의 원인은 php artisan config:cache 명령어에 있었습니다.
Laravel에서 config:cache를 실행하면:

  1. 모든 config 파일을 읽어서 하나의 파일로 합침
  2. bootstrap/cache/config.php 파일을 생성
  3. 이후로는 .env 파일을 읽지 않음
  4. env() 함수는 항상 null 반환 (의도된 동작)
  5. config() 함수만 캐시된 값을 정상적으로 반환

즉, Laravel은 성능 최적화를 위해 캐시 파일이 있으면 매번 .env 파일을 파싱하지 않습니다. 그래서 env() 함수는 작동하지 않고, config() 함수만 작동하는 것입니다.
왜 DB 연결은 정상이었을까?
Laravel의 database 설정은 이미 config 파일을 통해 관리되고 있습니다:

// config/database.php
'mysql' => [
    'host' => env('DB_HOST', '127.0.0.1'),
    'database' => env('DB_DATABASE', 'forge'),
    // ...
]

그래서 애플리케이션 코드에서는 다음과 같이 접근합니다:

// 이렇게 사용 (config를 통해)
config('database.connections.mysql.host')

config 파일을 거쳐서 사용하기 때문에 캐시가 있어도 정상 작동하는 것입니다.

env()와 config()의 차이

❌ 잘못된 사용법

// Blade 파일
<script>
const apiUrl = "{{ env('API_URL') }}";
</script>

// Controller
public function index()
{
    $host = env('FILESERVER_HOST');
    // ...
}

config:cache가 활성화되면 위 코드는 모두 null을 반환합니다.

✅ 올바른 사용법

1단계: config 파일 생성

// config/fileserver.php
<?php

return [
    'host' => env('FILESERVER_HOST', ''),
    'timeout' => env('FILESERVER_TIMEOUT', 30),
];

2단계: 애플리케이션에서 config() 사용

// Blade 파일
<script>
const config = {
    fileServerHost: "{{ config('fileserver.host') }}"
};
</script>

// Controller
public function index()
{
    $host = config('fileserver.host');
    $timeout = config('fileserver.timeout');
    // ...
}

Laravel Best Practice

Laravel 공식 문서에서도 명확하게 권장하는 패턴이 있습니다:

원칙

  • env() 함수는 config 파일 안에서만 사용
  • 애플리케이션 코드(Controller, Service, Blade 등)에서는 항상 config() 사용

이유

  1. 성능 최적화: config:cache로 설정을 캐싱하여 매번 .env 파일을 파싱하지 않음
  2. 일관성: 모든 설정을 한 곳에서 관리
  3. 타입 안정성: config 파일에서 기본값과 타입 변환 처리 가능
  4. 테스트 용이성: config 값을 모킹하기 쉬움
// config/services.php
return [
    'payment' => [
        'api_key' => env('PAYMENT_API_KEY'),
        'secret' => env('PAYMENT_SECRET'),
        'timeout' => (int) env('PAYMENT_TIMEOUT', 30), // 타입 변환
        'retry' => (bool) env('PAYMENT_RETRY', true),  // boolean 변환
    ],
];

추가 확인 사항: 파일 권한

config:cache 문제가 아닌데도 env() 값이 null이라면, .env 파일 권한을 확인해봐야 합니다.

# 파일 존재 및 권한 확인
$ ls -la .env
-rw-r----- 1 www-data www-data 873 Oct 14 00:19 .env

# tinker에서 확인
$ php artisan tinker
> file_exists(base_path('.env'))
= true
> is_readable(base_path('.env'))
= false  # ← 문제!

만약 is_readable()이 false라면, 현재 사용자가 .env 파일을 읽을 수 없는 것입니다.

해결 방법

# 방법 1: 파일 권한 변경
sudo chmod 640 .env

# 방법 2: 사용자를 웹서버 그룹에 추가
sudo usermod -aG www-data your-username
# 재로그인 또는
newgrp www-data

권한은 보안을 고려하여:

  • 644: 모든 사용자가 읽기 가능 (일반적)
  • 640: 소유자와 그룹만 읽기 가능 (더 안전)

해결 방법

단기 해결책: config:clear

당장 문제를 해결하려면 캐시를 제거하면 됩니다:

$ php artisan config:clear
Configuration cache cleared!

이제 env() 함수가 다시 작동합니다. 하지만 이 방법은:

  • ✅ 즉시 문제 해결
  • ⚠️ 매 요청마다 .env 파일을 읽어서 약간의 성능 저하
  • ⚠️ Best Practice에서 벗어남

장기 해결책: config() 사용으로 리팩토링 (권장)

올바른 방법은 코드를 Laravel의 권장 패턴으로 수정하는 것입니다:
1단계: 환경 변수를 config 파일로 옮기기

// config/app.php 또는 새 파일 생성
return [
    // 기존 설정들...
    
    'fileserver_host' => env('FILESERVER_HOST', ''),
    'api_url' => env('API_URL', ''),
    'external_service_key' => env('EXTERNAL_SERVICE_KEY', ''),
];

2단계: 코드 수정

// Before
{{ env('FILESERVER_HOST') }}
$url = env('API_URL');

// After
{{ config('app.fileserver_host') }}
$url = config('app.api_url');

3단계: 운영 환경에서 캐시 활성화

$ php artisan config:cache
Configuration cached successfully!

이제 성능도 좋고 Best Practice도 지킨 코드가 되었습니다!

정리

핵심 요약

  1. config:cache는 운영 환경 성능 최적화 기능
    • .env 파일을 매번 읽지 않고 캐시 사용
    • 캐시가 있으면 env() 함수는 null 반환
  2. env()는 config 파일에서만 사용
    • Controller, Service, Blade에서는 config() 사용
    • 이것이 Laravel의 공식 권장 패턴
  3. 파일 권한도 확인
    • .env 파일을 읽을 수 없으면 모든 env 값이 null
    • is_readable() 로 확인 가능
  4. 해결 방법 선택
    • 급할 때: config:clear
    • 올바른 방법: config 파일 생성 + 코드 리팩토링

개발 vs 운영 환경

# 개발 환경 (.env 실시간 반영 필요)
php artisan config:clear

# 운영 환경 (성능 최적화)
php artisan config:cache

처음에는 번거롭게 느껴질 수 있지만, config 파일을 통해 환경 변수를 관리하는 것이 장기적으로는 코드의 유지보수성과 성능 모두를 개선하는 방법입니다.
 

반응형