한우의 개발일기

[Next.js]next 프로젝트에 도커 적용기 본문

NextJS

[Next.js]next 프로젝트에 도커 적용기

한우코딩 2024. 11. 11. 15:28

트러블 슈팅

도커 빌드시 환경변수를 읽어 오지 못하는 오류가 생겼다

yml파일

# docker-compose.yml
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        NEXT_PUBLIC_API_MOCKING: ${NEXT_PUBLIC_API_MOCKING}
        NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_BASE_URL}
        NEXT_PUBLIC_KAKAOMAP_KEY: ${NEXT_PUBLIC_KAKAOMAP_KEY}
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - NEXT_PUBLIC_API_MOCKING=${NEXT_PUBLIC_API_MOCKING}
      - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL}
      - NEXT_PUBLIC_KAKAOMAP_KEY=${NEXT_PUBLIC_KAKAOMAP_KEY}
    env_file:
      - .env
    restart: always

이런식으로 설정을 해주고 docker compose config 명령어를 치면 환경변수가 잘 담기는데 빌드 과정에서 api 사용 부분에서 환경변수를 찾지 못하는 오류가 발생했다

이유는 우리 프로젝트는 fetch 함수를 커스텀 해서 사용중인데

/* eslint-disable @typescript-eslint/no-explicit-any */

import { ApiError, NotFoundError } from "@/custom-error";
import getBrowserCookie from "@/utils/browser-cookies";
import { getCookie } from "@/utils/next-cookie";

const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
if (!BASE_URL) throw new NotFoundError("base url이 없습니다.");

class CustomFetch {
  baseURL: string;
  accessToken?: string;

  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }

  async getAccessToken() {
    if (typeof window !== "undefined") {
      this.accessToken = getBrowserCookie("accessToken");
    } else {
      this.accessToken = await getCookie("accessToken");
    }
  }

  // 공통 fetch 메서드
  async fetchWithAuth<T>(
    endpoint: string,
    options: RequestInit = {},
  ): Promise<{ data: T }> {
    await this.getAccessToken();
    const headers = {
      ...options.headers,
      Authorization: `Bearer ${this.accessToken}`,
      "Content-Type": "application/json",
    };

    const config = {
      ...options,
      headers,
    };

    const response = await fetch(`${this.baseURL}${endpoint}`, config);

    if (!response.ok) {
      const errorData = await response.json();
      const error = new ApiError(
        errorData.message || "요청에 실패했습니다",
        response.status,
      );
      throw error;
    }

    return { data: await response.json() }; // Automatically return parsed JSON
  }

  // GET 메서드
  get<T>(endpoint: string, options: RequestInit = {}) {
    return this.fetchWithAuth<T>(endpoint, { ...options, method: "GET" });
  }

  // POST 메서드
  post<T>(endpoint: string, body: any, options: RequestInit = {}) {
    return this.fetchWithAuth<T>(endpoint, {
      ...options,
      method: "POST",
      body: JSON.stringify(body),
    });
  }

  // PUT 메서드
  put<T>(endpoint: string, body: any, options: RequestInit = {}) {
    return this.fetchWithAuth<T>(endpoint, {
      ...options,
      method: "PUT",
      body: JSON.stringify(body),
    });
  }

  // DELETE 메서드
  delete<T>(endpoint: string, options: RequestInit = {}) {
    return this.fetchWithAuth<T>(endpoint, { ...options, method: "DELETE" });
  }
}


const instance = new CustomFetch(BASE_URL);

export default instance;

API를 사용 하는 부분에서

export async function getShowsDetails(popupId: number): Promise<Show> {
  const { data } = await instance.get<Show>(`/api/popups/${popupId}`);
  return data;
}

NEXT_PUBLIC_BASE_URL를 찾지 못하는 오류가 발생했다

오류가나는 이류가 2가지 정도 있었는데
도커는 .env만 기본적으로 읽는다 .env.local 과 같은것들은 따로 설정을 해줘야 한다

const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
if (!BASE_URL) throw new NotFoundError("base url이 없습니다.");

그래서 커스텀 fetch 부분에서 baseUrl이 없다고 판단하여 에러를 던진다(빌드전에 파일을 한바퀴 돌고 baseUrl이 없다고 판단한다)

그래서 바꾼 방법은 .env.local을 .env 로 바꾸고

const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
if (!BASE_URL) throw new NotFoundError("base url이 없습니다.");

class CustomFetch {
  baseURL: string;
  accessToken?: string;

  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }

이거를

class CustomFetch {
  private baseURL: string;
  accessToken?: string;

  constructor() {
    const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
    if (!BASE_URL) {
      throw new NotFoundError("base url이 없습니다.");
    }
    this.baseURL = BASE_URL;
  }

  async getAccessToken() {
    if (typeof window !== "undefined") {
      this.accessToken = getBrowserCookie("accessToken");
    } else {
      this.accessToken = await getCookie("accessToken");
    }
  }

이렇게 클래스 안에서 Url 확인을 런타임 이후에 진행을 해준 다음 다시 빌드를 해주었다

성공 !!