문제 상황
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를 실행하면:
- 모든 config 파일을 읽어서 하나의 파일로 합침
- bootstrap/cache/config.php 파일을 생성
- 이후로는 .env 파일을 읽지 않음
- env() 함수는 항상 null 반환 (의도된 동작)
- 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() 사용
이유
- 성능 최적화: config:cache로 설정을 캐싱하여 매번 .env 파일을 파싱하지 않음
- 일관성: 모든 설정을 한 곳에서 관리
- 타입 안정성: config 파일에서 기본값과 타입 변환 처리 가능
- 테스트 용이성: 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도 지킨 코드가 되었습니다!
정리
핵심 요약
- config:cache는 운영 환경 성능 최적화 기능
- .env 파일을 매번 읽지 않고 캐시 사용
- 캐시가 있으면 env() 함수는 null 반환
- env()는 config 파일에서만 사용
- Controller, Service, Blade에서는 config() 사용
- 이것이 Laravel의 공식 권장 패턴
- 파일 권한도 확인
- .env 파일을 읽을 수 없으면 모든 env 값이 null
- is_readable() 로 확인 가능
- 해결 방법 선택
- 급할 때: config:clear
- 올바른 방법: config 파일 생성 + 코드 리팩토링
개발 vs 운영 환경
# 개발 환경 (.env 실시간 반영 필요)
php artisan config:clear
# 운영 환경 (성능 최적화)
php artisan config:cache
처음에는 번거롭게 느껴질 수 있지만, config 파일을 통해 환경 변수를 관리하는 것이 장기적으로는 코드의 유지보수성과 성능 모두를 개선하는 방법입니다.
'개발 & IT > 백엔드' 카테고리의 다른 글
| MySQL 타임존 SYSTEM 설정과 UTC 저장 문제 해결하기 (0) | 2025.10.30 |
|---|---|
| Sequelize vs Prisma: Node.js ORM 비교 분석 (0) | 2025.10.26 |
| Prisma로 Next.js 블로그 만들기: 데이터베이스 설정부터 마이그레이션까지 (0) | 2025.10.26 |
| Let's Encrypt 인증서 자동 갱신 완벽 가이드 (1) | 2025.10.14 |
| nginx SSL 설정 중 만난 DNS 캐시 트러블슈팅 (0) | 2025.10.14 |