5 분 소요

◆환경변수(Environment Variable)란?

환경변수(Environment Variable)는 프로그램의 코드와 설정값을 분리하기 위해 사용하는 값이다.

예를 들어 Supabase의 URL을 코드에 직접 작성할 수도 있다.

const supabaseUrl = 'https://xxxxx.supabase.co'

하지만 실제 프로젝트에서는 다음과 같이 환경변수를 사용하는 것이 좋다.

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL

그리고 환경변수 파일에서 실제 값을 관리한다.

VITE_SUPABASE_URL=https://xxxxx.supabase.co

이렇게 하면 코드 자체를 수정하지 않고 환경에 따라 다른 설정값을 사용할 수 있다.


◆환경변수를 사용하는 이유

환경변수를 사용하는 이유는 크게 다음과 같다.

① 환경별 설정을 분리할 수 있다

개발 환경과 운영 환경에서 서로 다른 서버나 DB를 사용할 수 있다.

개발 환경
React → 개발용 서버/DB

운영 환경
React → 운영용 서버/DB

예를 들어 개발 환경에서는:

VITE_API_URL=http://localhost:8080

운영 환경에서는:

VITE_API_URL=https://api.example.com

처럼 서로 다른 값을 사용할 수 있다.


② 코드와 설정을 분리할 수 있다

소스 코드에 URL이나 설정값을 직접 작성하지 않아도 된다.

const apiUrl = import.meta.env.VITE_API_URL

이렇게 작성하면 실제 URL은 환경변수 파일에서 관리할 수 있다.


③ 민감한 정보를 코드와 분리할 수 있다

서버에서만 사용해야 하는 비밀키도 환경변수로 관리할 수 있다.

예:

SUPABASE_SERVICE_ROLE_KEY=xxxxx
BREVO_API_KEY=xxxxx

다만 환경변수에 넣었다고 해서 무조건 비밀 정보가 되는 것은 아니다.

특히 Vite에서는 VITE_가 붙은 환경변수가 브라우저에서 사용할 수 있도록 빌드 결과에 포함될 수 있기 때문에 주의해야 한다.


◆.env 파일의 종류

Vite 프로젝트에서는 다음과 같은 환경변수 파일을 사용할 수 있다.

.env
.env.local
.env.development
.env.production

파일 이름에 따라 적용되는 환경이 달라질 수 있다.

예를 들어:

npm run dev
     ↓
development 환경
     ↓
.env.development

그리고:

npm run build
     ↓
production 환경
     ↓
.env.production

과 같이 사용할 수 있다.


◆.env.development와 .env.production은 왜 나누는가?

개발 환경과 운영 환경의 설정값이 다를 수 있기 때문이다.

예를 들어 개발 환경에서는:

VITE_API_URL=http://localhost:8080

운영 환경에서는:

VITE_API_URL=https://api.example.com

을 사용할 수 있다.

즉,

개발 환경
localhost:8080

운영 환경
api.example.com

처럼 서로 다른 서버를 사용하기 위해 환경변수를 분리하는 것이다.


◆두 파일을 반드시 만들어야 할까?

아니다.

.env.development.env.production은 반드시 둘 다 만들어야 하는 파일이 아니다.

개발 환경과 운영 환경에서 사용하는 값이 동일하다면 하나의 공통 환경변수 파일을 사용할 수도 있다.

예를 들어 하나의 Supabase 프로젝트를 개발과 운영에서 모두 사용한다면:

VITE_SUPABASE_URL=https://xxxxx.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=xxxxx

와 같이 동일한 값을 사용할 수 있다.

환경변수를 환경별로 나누는 이유는 개발 환경과 운영 환경의 설정값이 다를 수 있기 때문이다.


◆VITE_는 무엇인가?

Vite 프로젝트에서는 브라우저에서 사용할 환경변수를 구분하기 위해 일반적으로 VITE_ 접두사를 사용한다.

예를 들어:

VITE_SUPABASE_URL=https://xxxxx.supabase.co

그리고 React에서는:

import.meta.env.VITE_SUPABASE_URL

로 사용할 수 있다.

반면 다음과 같이 VITE_가 없는 환경변수는:

SUPABASE_SERVICE_ROLE_KEY=xxxxx

브라우저 코드에서 import.meta.env.SUPABASE_SERVICE_ROLE_KEY 방식으로 사용할 수 있는 것이 아니다.

즉, Vite의 클라이언트 코드에 노출할 필요가 있는 값과 서버에서만 사용해야 하는 값을 구분하는 데 VITE_ 접두사를 활용할 수 있다.


◆VITE_SUPABASE_URL은 무엇인가?

다음과 같은 환경변수가 있다고 하자.

VITE_SUPABASE_URL=https://xxxxx.supabase.co

이 값을 처음 보면 “Vercel과 Supabase를 연결하는 키인가?”라고 생각할 수 있다.

하지만 정확하게 말하면 Supabase 프로젝트의 URL이다.

즉, React 애플리케이션에게:

“어느 Supabase 프로젝트에 접속해야 하는가?”

를 알려주는 주소이다.

VITE_SUPABASE_URL
        ↓
Supabase 프로젝트 주소
        ↓
https://xxxxx.supabase.co

◆Supabase 연결에는 URL과 Key가 함께 필요하다

React에서 Supabase를 사용하려면 일반적으로 URL과 Publishable Key를 함께 사용한다.

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY
)

각 값의 역할은 다음과 같다.

VITE_SUPABASE_URL
        ↓
어느 Supabase 프로젝트에 접속할 것인가?

VITE_SUPABASE_PUBLISHABLE_KEY
        ↓
브라우저에서 사용할 공개용 키

전체적인 구조는 다음과 같다.

React
  │
  ├── VITE_SUPABASE_URL
  │
  └── VITE_SUPABASE_PUBLISHABLE_KEY
          │
          ▼
       Supabase
          │
          ▼
      PostgreSQL

◆VITE_SUPABASE_PUBLISHABLE_KEY란?

VITE_SUPABASE_PUBLISHABLE_KEY브라우저에서 Supabase를 사용하기 위한 공개용 키이다.

예를 들어:

VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxxx

그리고 React에서:

const supabase = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY
)

와 같이 사용한다.

여기서 중요한 점은 Publishable Key는 브라우저에 노출될 수 있다는 것이다.

왜냐하면 VITE_가 붙은 환경변수는 Vite 빌드 과정에서 클라이언트 코드에서 사용할 수 있도록 처리되기 때문이다.

따라서 Publishable Key를 사용하는 것 자체가 보안 취약점은 아니다.

대신 Supabase에서는 RLS(Row Level Security)를 통해 실제 데이터 접근 권한을 제어해야 한다.

브라우저
   │
   │ Publishable Key
   ▼
Supabase
   │
   │ RLS 정책 확인
   ▼
데이터 접근 허용/차단

즉, Publishable Key 자체가 데이터베이스 접근 권한을 모두 허용하는 것은 아니다.


◆SUPABASE_SERVICE_ROLE_KEY란?

반면 SUPABASE_SERVICE_ROLE_KEY는 완전히 다르게 취급해야 한다.

이 키는 서버 측에서만 사용해야 하는 매우 민감한 키이다.

예:

SUPABASE_SERVICE_ROLE_KEY=xxxxx

이 키는 브라우저의 React 코드에서 사용하면 안 된다.

특히 다음과 같이 작성하면 안 된다.

VITE_SUPABASE_SERVICE_ROLE_KEY=xxxxx

왜냐하면 VITE_를 붙이면 클라이언트 코드에 노출될 수 있기 때문이다.


◆Publishable Key와 Service Role Key의 차이

두 키의 가장 중요한 차이는 사용 위치와 권한이다.

구분 Publishable Key Service Role Key
환경변수 예시 VITE_SUPABASE_PUBLISHABLE_KEY SUPABASE_SERVICE_ROLE_KEY
사용 위치 브라우저 / React 서버
VITE_ 사용 O X
브라우저 노출 가능 절대 금지
RLS 적용됨 RLS를 우회할 수 있음
용도 일반적인 클라이언트 Supabase 접근 서버의 관리자 수준 작업
보안 수준 공개 가능 비밀 유지 필요

쉽게 표현하면 다음과 같다.

Publishable Key
      ↓
브라우저에서 사용
      ↓
RLS 정책 적용
      ↓
허용된 데이터만 접근

반면:

Service Role Key
      ↓
서버에서만 사용
      ↓
관리자 수준 권한
      ↓
RLS를 우회할 수 있음

따라서 Service Role Key가 Publishable Key보다 강력한 권한을 가진다.


◆Service Role Key를 React에서 사용하면 안 되는 이유

React 애플리케이션은 최종적으로 사용자의 브라우저에서 실행된다.

따라서 React 코드에 비밀키를 넣으면 사용자가 개발자 도구 등을 통해 해당 값을 확인할 가능성이 있다.

잘못된 예:

VITE_SUPABASE_SERVICE_ROLE_KEY=xxxxx
const supabase = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_SERVICE_ROLE_KEY
)

이렇게 하면 Service Role Key가 클라이언트에 노출될 수 있다.

따라서 Service Role Key가 필요한 작업은 서버에서 처리해야 한다.

React
  │
  │ Publishable Key
  ▼
Supabase
  │
  └── 일반적인 데이터 접근

관리자 권한이 필요한 작업은:

React
  │
  ▼
서버 / Edge Function
  │
  │ Service Role Key
  ▼
Supabase

와 같은 구조로 처리하는 것이 적절하다.


◆Publishable Key도 아무렇게나 사용해도 되는 것은 아니다

Publishable Key가 공개용이라고 해서 Supabase 테이블을 아무나 수정할 수 있는 것은 아니다.

실제 보안은 RLS 정책이 담당한다.

예를 들어 posts 테이블이 있다고 하자.

React
  │
  │ Publishable Key
  ▼
Supabase
  │
  ▼
posts 테이블

이때 RLS 정책을 통해:

SELECT → 허용
INSERT → 특정 사용자만 허용
UPDATE → 작성자만 허용
DELETE → 관리자만 허용

처럼 세부적인 권한을 설정할 수 있다.

따라서 Supabase를 브라우저에서 직접 사용하는 구조에서는 Publishable Key + 적절한 RLS 정책이 핵심이다.


◆.gitignore에 환경변수 파일을 추가한다

실제 환경변수 파일에는 중요한 값이 포함될 수 있으므로 일반적으로 Git에 올리지 않는다.

예:

.env
.env.*
!.env.example

그러면:

.env
.env.development
.env.production
.env.local

등은 Git에서 제외하고,

.env.example

은 Git에 포함할 수 있다.


◆.env.example을 사용하는 이유

팀 프로젝트에서는 다른 개발자가 프로젝트를 실행하기 위해 어떤 환경변수가 필요한지 알아야 한다.

그래서 실제 값은 제거한 .env.example 파일을 Git에 올릴 수 있다.

예:

# .env.example

VITE_SUPABASE_URL=
VITE_SUPABASE_PUBLISHABLE_KEY=

SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=

BREVO_API_KEY=
MAIL_FROM=
MAIL_FROM_NAME=
HOTEL_INQUIRY_ADMIN_EMAIL=

여기에는 실제 키 값을 작성하지 않는다.

다른 개발자는:

.env.example
      ↓
.env.development
      ↓
실제 개발용 값 입력

과 같은 방식으로 사용할 수 있다.


◆Vercel에서는 환경변수를 어떻게 관리하는가?

Vercel에 React 프로젝트를 배포하는 경우에는 실제 운영 환경변수를 Vercel의 프로젝트 설정에서 관리할 수 있다.

예를 들어:

Vercel
  ↓
Project
  ↓
Settings
  ↓
Environment Variables

여기에 다음과 같은 값을 설정할 수 있다.

VITE_SUPABASE_URL
VITE_SUPABASE_PUBLISHABLE_KEY

그리고 서버에서만 사용하는 값은:

SUPABASE_SERVICE_ROLE_KEY
BREVO_API_KEY

등으로 관리한다.

여기서도 중요한 것은 VITE_가 Vercel에서 붙이는 접두사가 아니라 Vite가 클라이언트 환경변수를 구분하기 위해 사용하는 규칙이라는 점이다.


댓글남기기