해결 방법

.env.local 파일을 사용하고 접두사로 NEXT_PUBLIC_ 을 사용한다!

👉 NEXT_PUBLIC_PROJECT_ID

에러 발생

Next.JS 에서 .env 파일에 환경 변수를 작성해 사용하던 중 에러가 발생했다.
클라이언트 컴포넌트에서 환경 변수를 호출했는데 Node.JS 기반의 터미널에서는 정상적으로 출력되면서 브라우저 콘솔에서는 undefined 가 출력되는 것이었다.
게다가 토큰을 포함한 중요 데이터들을 환경 변수로 선언한 후에 Sanity Studio 에서 데이터를 가져오는 API 코드를 작성하는 과정에서 계속해서 환경 변수로 선언한 데이터가 존재하지 않는 에러만 보여줄 뿐이었다.

// .env
SANITY_DATASET=
SANITY_PROJECT_ID=
SANITY_TOKEN=

해결 과정

이 문제에 대해 나는 먼저 공식 사이트를 이용했다.
Next.JS 공식 문서에는 이렇게 나와있다.

Configuring: Environment Variables

.env.local 파일을 사용하고 접두사로 NEXT_PUBLIC_ 을 사용할 것

  1. 먼저 현재 내가 사용중인 환경 변수 파일은 .env 파일이었기 때문에 .env.local 파일로 변경해주었다.
    console.log 를 이용해 출력했을 때는 환경 변수의 값이 제대로 출력되었지만 Sanity 의 데이터를 가져온 페이지에서는 계속해서 에러가 발생했다.
// .env.local
SANITY_DATASET=
SANITY_PROJECT_ID=
SANITY_TOKEN=
  1. 그래서 그 다음엔 접두사로 NEXT_PUBLIC 을 사용했다.
// .env.local
NEXT_PUBLIC_SANITY_DATASET=
NEXT_PUBLIC_SANITY_PROJECT_ID=
SANITY_TOKEN=

사용할 때에도 접두사로 NEXT_PUBLIC_ 을 붙여주었다.

export const client = createClient({
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
	//...
})

그랬더니 환경 변수를 정상적으로 불러와서 API 요청이 정상적으로 처리될 수 있었다.

고찰 With Deep Dive

그런데 여기서 궁금한 점이 몇가지 있었다.

  1. Node.JS 기반의 Terminal 에서는 환경변수가 .env 이든, .env.local 이든 상관없이 모두 잘 출력되었으나 왜 브라우저의 개발자 도구 콘솔에서는 undefined 가 출력되었을까?

  2. 접두사로 NEXT_PUBLIC_ 을 사용하면 브러우저에 환경 변수를 노출 할 수 있다고 공식문서에 적혀있는데, 브라우저에 환경 변수를 노출하면 보안상으로 좋지 않은 것이 아닌가?

  3. 나는 ‘use client’ 가 적혀있는 클라이언트 컴포넌트에서 console.log 를 사용했는데 왜 Node.JS 기반의 Terminal 에서도 출력이 된 것일까? (나는 Next.JS에서 클라이언트 컴포넌트서버 컴포넌트를 구분하기 위해 ‘use client’ 를 사용하는 방법도 있었지만, console.log 를 사용해보면 확실하게 확인할 수 있다고 알고있었기 때문이다. 그래서 console.log 를 사용하면 터미널에서 출력된 경우 서버 컴포넌트이고, 브라우저의 개발자 도구 콘솔에서 출력된 경우 클라이언트 컴포넌트 라고 알고있었다.)

하나씩 알아보자.

1. Node.JS 기반의 Terminal 에서는 환경변수가 .env 이든, .env.local 이든 상관없이 모두 잘 출력되었으나 왜 브라우저의 개발자 도구 콘솔에서는 undefined 가 출력되었을까?

환경 변수는 일반적으로 Next.js의 서버 측에서 사용된다.
클라이언트 측에서는 환경 변수에 직접 접근할 수 없으며, 해당 변수의 값을 직접 클라이언트 컴포넌트에서 출력할 수 없다.
따라서 개발자 도구의 콘솔에서 undefined 값이 출력되는 것은, 클라이언트 측에서 직접 환경 변수에 접근하려고 했기 때문이다.

환경 변수는 보안상의 이유로 클라이언트 측에 노출되지 않으며, 서버 측에서 값을 직접 가져와야 한다.
그러나 클라이언트 측에서 환경 변수를 아예 접근할 수 없는 것은 아니다.
접두사 NEXT_PUBLIC_을 사용하여 클라이언트 측에 환경 변수를 전달할 수 있다.
또한 서버 측 코드에서 해당 환경 변수를 가져와서 클라이언트로 전달하는 경우에는 접근이 가능하다.
일반적으로 next.config.js 파일을 사용하여 환경 변수를 설정하고, publicRuntimeConfig를 통해 클라이언트 측에서 사용할 수 있도록 해야한다.

module.exports = {
  publicRuntimeConfig: {
    SANITY_DATA: process.env.SANITY_DATA,
  },
};

이런 식으로 선언된 환경 변수는 클라이언트 컴포넌트에서 사용할 수 있다.
단, next/config 를 사용해서 말이다.

import getConfig from 'next/config';

const { publicRuntimeConfig } = getConfig();
const envData = publicRuntimeConfig.SANITY_DATA;
console.log(envData);

이렇게 한다면 브라우저의 개발자 도구 콘솔에서 환경변수의 값을 정상적으로 출력할 수 있다.

👀 여기서 좀 더 깊게 Deep Dive

.env 파일에 설정된 변수는 모든 환경에서 사용할 수 있는 것은 아니다. 예를 들어, 위에서 얘기했듯 개발 서버에서 실행되는 클라이언트 측 코드는 .env 파일에 설정된 변수를 사용할 수 없다. 이는 Next.js의 환경 변수 로딩 메커니즘이 서버 측에서만 동작하기 때문이다.

서버 측 코드에서 환경 변수를 사용하려면, .env 파일 또는 .env.local 파일을 사용해야 한다. 이 파일은 개발 서버에서 환경 변수로 로드되며, 서버 측에서 사용할 수 있다. 그런데 여기서 주의할 점은 바로 환경 변수 로드 순서이다.

Next.JS 공식 문서 - 환경 변수 로드 순서

이 문서를 확인하면 process.env -> .env.local -> .env 순서로 환경 변수가 로드된다는 것을 알 수 있다.
따라서 .env 파일과 .env.local 파일을 모두 사용하고 있는 상황에서 같은 환경 변수에 다른 값이 들어가있다면 이를 유의해야한다.

2. 접두사로 NEXT_PUBLIC_ 을 사용하면 브러우저에 환경 변수를 노출 할 수 있다고 공식문서에 적혀있는데, 브라우저에 환경 변수를 노출하면 보안상으로 좋지 않은 것이 아닌가?

Next.JS 공식 문서 - 환경 변수 브라우저 노출

Next.js에서 환경 변수를 클라이언트 측에서 사용하기 위해 나는 접두사NEXT_PUBLIC_ 를 사용했다.
NEXT_PUBLIC_SANITY_PROJECT_ID와 같이 .env.local 파일에 환경 변수를 정의할 때 접두사NEXT_PUBLIC_ 를 붙여야 한다.
그리고 클라이언트 측에서는 process.env.NEXT_PUBLIC_SANITY_PROJECT_ID를 사용하여 해당 환경 변수 값을 가져와야 했다.

클라이언트 측에서 사용되는 환경 변수는 기본적으로 브라우저에 노출되기 때문에 클라이언트 측에서 사용되는 환경 변수는 민감한 정보를 포함해서는 안된다.
민감한 정보가 필요한 경우에는 해당 정보를 클라이언트에 직접 노출시키지 않고, 서버 측에서 처리하고 클라이언트로 전달하는 방법을 사용해야 한다.
(일반적으로 API 엑세스 키나 프로젝트 ID와 같은 공개 정보는 클라이언트 측에서 사용할 수 있다. -> 나도 SANITY의 ProjectID와 Dataset ID 만을 클라이언트 측에서 사용할 수 있게 하였다는 점에서 이 부분을 지켰다고 볼 수 있다.)

👀 여기서 좀 더 깊게 Deep Dive

그렇다면 SANITY ProjectID는 공개해도 상관이 없을까?

SANITY ProjectID는 실제 데이터에 접근하기 위해 필요한 정보이기 때문에 공개할 경우 보안에 취약할 수 있다고 생각할 수 있지만, 내가 SANITY STUDIO를 사용하면서 느낀 점은 딱히 상관 없을 것 같다는 것이다.
SANITY의 데이터에 대해 CORS 를 하기 위해서는 ProjectId 뿐만 아니라 Dataset, Token 등이 필요한데, 여기서 토큰을 통해 읽기 권한, 읽고 쓰기 권한 등을 부여하기 때문이다.
따라서 SANITY 프로젝트 ID를 클라이언트 측에서 사용하는 것은 일반적으로 보안상의 문제를 일으키지 않을 것 같다는 게 나의 생각이다.

여기서 명심해야 할 점은 방금 얘기했듯 중요한 데이터 중 하나인 Token 은 절대로 노출시켜서는 안된다는 것이다. Token 뿐만 아니라 인증 관련 정보는 클라이언트측에 전달하지 않고 서버 측에서만 사용하는 것이 좋을 듯 싶다.

Stack Overflow - 환경 변수 관련 질문

3. 나는 ‘use client’ 가 적혀있는 클라이언트 컴포넌트에서 console.log 를 사용했는데 왜 Node.JS 기반의 Terminal 에서도 출력이 된 것일까? (나는 Next.JS에서 클라이언트 컴포넌트서버 컴포넌트를 구분하기 위해 ‘use client’ 를 사용하는 방법도 있었지만, console.log 를 사용해보면 확실하게 확인할 수 있다고 알고있었기 때문이다. 그래서 console.log 를 사용하면 터미널에서 출력된 경우 서버 컴포넌트이고, 브라우저의 개발자 도구 콘솔에서 출력된 경우 클라이언트 컴포넌트 라고 알고있었다.)

일반적으로 클라이언트 컴포넌트에서 console.log를 호출하면, 해당 로그는 브라우저의 개발자 도구 콘솔에 출력된다.
그러나 "use client"라는 주석이 달린 클라이언트 컴포넌트에서 console.log를 호출했을 때 터미널에서도 출력되는 이유는, 개발 서버에서의 동작과 관련이 있다.
개발 서버(npm run dev)에서는 Next.js 애플리케이션을 실행하기 위해 Node.js 환경을 사용하는데, 개발 서버는 클라이언트 측 컴포넌트의 코드를 번들링하여 브라우저에서 실행될 수 있도록 한다.
따라서 개발 서버(npm run dev)에서는 클라이언트 측 컴포넌트의 코드를 실행하기 위해 Node.js 환경에서 일시적으로 실행된다.

"use client" 주석이 달린 클라이언트 컴포넌트의 console.log는 개발 서버(npm run dev)에서의 동작으로 Node.js 환경에서 실행되기 때문에 Node.JS 기반의 터미널과 브라우저의 개발자 도구 콘솔 양쪽에서console.log 가 실행되어 데이터가 출력되었다는 것을 알 수 있다.


마치며

오랜만에 블로그 포스팅을 했다.

공부 내용, 해결한 에러들에 관한 모든 내용을 노션과 구글 문서에 매번 정리해왔지만 블로그 포스팅은 어째서인지 꾸준히 작성하지 못했다.

1년간 책을 굉장히 많이 읽으며 성장했지만 결국 가장 중요한 것은 실천이다.

어제 『소프트 스킬』을 읽던 도중 블로그 포스팅에 관한 내용이 나왔다.

블로그 포스트는 자신을 마케팅 하는 가장 쉬운 방법이다.

이 말이 내 머리를 세게 때렸다.
지금까지 미친듯이 살아왔지만 이제 깨달은 내용들도 실천해가며 살기로 했다.
그래서 나는 오늘부터 최소 주 1회 포스팅을 실천할 것이다.

더 많은 것들을 깨달은 뒤 포스팅을 하니 굉장히 유익했다.

블로그 포스팅은 나에게 있어서 내가 아는 무언가를 남에게 설명하는 공간이다.
남에게 설명하기 위해서는 더 깊게 공부하고 더 많이 알아야 하며 확실하고 정확한 정보를 갖고 있어야 한다.
그 덕분에 단 하나의 에러만을 다루었는데도 정말 많은 것들을 깨달았다.

신이 나를 얼마나 위대하게 쓰려고 이런 고난을 주는 걸까 - 『역행자』

profile
한 번 시작하면 끝까지 물고 늘어지는 개발자입니다.

0개의 댓글