[Node.js] dotenv.config(); 실행 전 다른 파일이 먼저 읽히는 문제

2024. 10. 23. 18:54·만들어보기/TroubleShooting

문제 상황

Node.js(+express)에 AWS S3를 연결하면서 .env 파일에 다음과 같이 환경변수를 추가하였다.

# AWS S3
AWS_ACCESS_KEY=비밀
AWS_ACCESS_SECRET_KEY=비밀
AWS_REGION=ap-northeast-2
AWS_S3_BUCKET_NAME=비밀

그리고 이 환경변수를 s3 configuration 파일에서 읽어와야 하는데, 제대로 읽어오지 못 해서 계속해서 undefined로 값이 들어가는 상황이 발생하였다.

문제의 파일 's3Client.js'

import { S3Client } from '@aws-sdk/client-s3';

const s3 = () =>
	new S3Client({
		region: process.env.AWS_REGION,
		credentials: {
			accessKeyId: process.env.AWS_ACCESS_KEY,
			secretAccessKey: process.env.AWS_ACCESS_SECRET_KEY,
		},
	});

export default s3;

콘솔을 파일에 찍어서 확인해 본 결과,

index.js 의 `dotenv.config();` 구문이 실행되기 전에 위의 s3Client.js 파일이 먼저 읽히고 있었다.

 

이를 해결하기 위해 dotenv.config(); 구문을 가장 위로 올려보기도 하였으나 먹히지 않았다 (ㅎ;;)

index.js

import dotenv from 'dotenv';
dotenv.config();

import express from 'express';
import pool from './config/psql.js';
import cookieParser from 'cookie-parser';
import authRouter from './routes/authRouter.js';
import jwtMiddleware from './lib/jwtMiddleware.js';
import oauthRouter from './routes/oauthRouter.js';
import articleRouter from './routes/articleRouter.js';
import commentRouter from './routes/commentRouter.js';
import memberRouter from './routes/memberRouter.js';
import imageRouter from './routes/imageRouter.js';

const app = new express();

app.set('port', process.env.PORT || 4000);
app.use(express.json());
app.use(cookieParser());

// 라우터 등록
app.use(jwtMiddleware);
app.use('/api/auth', authRouter);
app.use('/api/oauth', oauthRouter);
app.use('/api/article', articleRouter);
app.use('/api/comment', commentRouter);
app.use('/api/member', memberRouter);
app.use('/api/image', imageRouter);

app.listen(app.get('port'), () => {
	console.log(`Listening to ${app.get('port')} port ...`);
});

// 서버 종료 시 Pool 종료
process.on('SIGINT', async () => {
	await pool.end();
	console.log('\nPool had ended.');
	process.exit();
});

이렇게 .. 해보았음 그러나 실패

해결 방법

열심히 구글링을 하다 들리게 된 한 블로그 덕분에 이 문제를 해결할 수 있었다.

 

현재 ES 모듈을 사용하고 있어 생긴 문제였으며,

문제 해결의 키는 dotenv.config(); 를 실행하는 파일을 따로 두어서 이 파일을 index.js 에서 불러오는 것이다.

env.js

import dotenv from 'dotenv';

dotenv.config();

위 파일을 index.js 에서 가장 먼저 불러오도록 수정하니 바로 문제가 해결되었다.

변경된 index.js 부분

import './env.js';

 

알아보기

그렇다면 ES 모듈과 CommonJS가 파일을 읽어오는 방식에 어떤 차이가 있을까?

 

CommonJS는 동기적으로 모듈을 로드한다.

즉, require()을 통해 어떤 모듈이 전부 읽혀지기 전에는 코드의 실행이 멈춘다.

 

이와 반대로, ES 모듈은 비동기적으로 모듈을 로드한다.

ES 모듈은 1. import 구문들을 먼저 최상단으로 호이스팅 한 후에 2. 해당 구문들을 비동기적으로 로드한다.

 

이제 이것을 알고나서, 나의 예전 index.js 코드를 살펴보자 

import dotenv from 'dotenv';
dotenv.config();

import express from 'express';
import pool from './config/psql.js';
import cookieParser from 'cookie-parser';
import authRouter from './routes/authRouter.js';
import jwtMiddleware from './lib/jwtMiddleware.js';
import oauthRouter from './routes/oauthRouter.js';
import articleRouter from './routes/articleRouter.js';
import commentRouter from './routes/commentRouter.js';
import memberRouter from './routes/memberRouter.js';
import imageRouter from './routes/imageRouter.js';

const app = new express();

app.set('port', process.env.PORT || 4000);
app.use(express.json());
app.use(cookieParser());

// 라우터 등록
app.use(jwtMiddleware);
app.use('/api/auth', authRouter);
app.use('/api/oauth', oauthRouter);
app.use('/api/article', articleRouter);
app.use('/api/comment', commentRouter);
app.use('/api/member', memberRouter);
app.use('/api/image', imageRouter);

app.listen(app.get('port'), () => {
	console.log(`Listening to ${app.get('port')} port ...`);
});

// 서버 종료 시 Pool 종료
process.on('SIGINT', async () => {
	await pool.end();
	console.log('\nPool had ended.');
	process.exit();
});

 

dotenv.config(); 를 맨 위로 올려놓아도 먼저 실행되지 않는 이유를 이제 알게 된다.

ES 모듈은 import 구문들을 먼저 최상단으로 호이스팅 하기 때문에,

어느 위치에 있든 해당 구문은 import로 모듈들이 로드되고 난 후 실행되게 된다.

 

그리고 문제의 s3Client.js 파일은 index.js 파일에서 로드되는 imageRouter.js 내에 또 import가 되어있다.

따라서 dotenv.config(); 구문 이전에 읽혀지게 되며 이로 인해 .env 내의 파일들이 환경변수로 설정이 되기 전에 해당 변수에 접근하게 되어 undefined로 읽힌다.

 

⭐️ 따라서 index 파일에서 import 호이스팅이 실행될 때 dotenv.config(); 이 함께, 그리고 먼저 실행되도록 파일로 따로 빼어서 가장 먼저 import 처리해주어야 하는 것이다.

여기서 잠깐 ...

이렇게 따로 빼준 env.js 파일을 import 문 가장 뒷쪽에 배치한다면 어떻게 될까?

가장 먼저 import문으로 선언해주었던 import './env.js'; 를 가장 뒷쪽으로 빼보았다.

 

그랬더니

 

ㅇㅇ 에러가 난다

저작자표시 비영리 변경금지 (새창열림)

'만들어보기 > TroubleShooting' 카테고리의 다른 글

[TroubleShooting] PDF 프리뷰시 자동 다운로드 현상 해결하기  (2) 2025.08.08
[SpringBoot] SQS 메시지 발행 병목 @Async + Executor로 해결하기  (0) 2025.05.16
[TroubleShooting] MySQL 락을 건 적이 없는데 데드락이 발생한다  (0) 2025.04.06
'만들어보기/TroubleShooting' 카테고리의 다른 글
  • [TroubleShooting] PDF 프리뷰시 자동 다운로드 현상 해결하기
  • [SpringBoot] SQS 메시지 발행 병목 @Async + Executor로 해결하기
  • [TroubleShooting] MySQL 락을 건 적이 없는데 데드락이 발생한다
다섯자두
다섯자두
All I need is 💻 , ☕️ and a dash of luck
  • 다섯자두
    subbni
    다섯자두
  • 전체
    오늘
    어제
    • 전체 글 (89)
      • 개발 이야기 (0)
      • 만들어보기 (17)
        • FromBookToBook (5)
        • Spring (5)
        • Node.js & React (3)
        • TroubleShooting (4)
      • 공부하기 (72)
        • Network (3)
        • Cloud (1)
        • Database (5)
        • Java (13)
        • Javascript (0)
        • Spring (9)
        • React (18)
        • Algorithm (8)
        • 자료구조 (7)
        • ETC (8)
      • 회고 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • velog
  • 공지사항

  • 인기 글

  • 태그

    재시도 로직
    network
    SQS
    알림 기능
    Database
    Til
    pdf 자동 다운로드
    redis
    SQL
    aws
    mysql
    java
    HTTP
    Spring
    springboot
    실시간 데이터 전송 기술
    티스토리챌린지
    최단거리
    JPA
    outbox
    오블완
    Express
    SSE
    pdf 프리뷰 실패
    알고리즘
    서명알고리즘
    프로젝트
    로그인
    outbox 패턴
    자료구조
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
다섯자두
[Node.js] dotenv.config(); 실행 전 다른 파일이 먼저 읽히는 문제
상단으로

티스토리툴바