[프론트엔드] 블로그 리팩토링 - BFF 아키텍처

2026.02.04. 17:52

1. 문제 인식

개발환경이 전환되는 등 상황에서 계속적으로 api 상태가 불안정하다. 

그리고 SSR과 CSR이 혼재가 되어있는 상황에서 API 요청의 baseURL이 이원화되는 문제가 있어 관리가 어렵다. 

-> 어떤 API가 CSR에서 요청되는지, SSR에서 요청되는지를 분류해야한다. 

초기에는 집에서만 개발하는 것을 상정했으나 점차적으로 다른 컴퓨터 등에서 시연하거나 개발해야하는 경우를 염두에 두지 않을 수 없고 

추후 유지보수성을 고려했을 때 전체적으로 리팩토링이 필요한 상황이다. 

구체적인 문제점

  • 백엔드로 가야하는 API 요청이 자꾸 프론트 루트로 감. 이 문제가 정확히 nginx 문제인지, 환경변수 문제인지 알아내기 어려운 부분이 있음.

  • 이원화된 베이스url 관리. 지금 SSR요청용 url(컨테이너 url)과 CSR요청용 url(도메인) 등이 이원화 되어있고 어느 로직에 이를 적용해야할지 불분명하거나 수정에 따라 에러가 발생함. -> 유지보수 어려움

  • nginx 과대화. nginx가 인프라 영역을 넘어 게이트웨이 역할+라우팅을 하다보니 로직이 혼재되고 커짐. 환경변수도 과도화됨.

결론 

현재 원인은 프론트가 API 게이트웨이 역할을 하지 못하고 nginx가 그 역할을 맡고 있어서 생기는 문제. 즉 어플리케이션 계층에서 API 경로제어를 하지 않고 인프라(nginx) 계층에서 경로제어를 하는 것이 문제.

--> 어플리케이션 계층에서 API 경로제어를 위한 계층을 추가해야함. => BFF 패턴

2.  BFF 패턴 

https://miro.medium.com/v2/resize%3Afit%3A1400/1%2A2BaGJecjJNBk0gGCUQJO2w.png

모든 요청이 프론트엔드에 부속되어있는 라우트 게이트웨이를 통해 이뤄짐.

=> 인프라 레벨에서의 api 라우팅이 아니라 어플리케이션 계층에서 수정됨. 

인프라 계층인 nginx에서는 불필요한 리다이렉션 코드를 유지할 필요없고 프론트에서도 이원화된 코드를 유지할 필요 없음.

3. 구체적인 코드 예시

'use server'
import { jsonApi} from "@/lib/axios";
import { AxiosResponse } from "axios";



export interface SaveCommentReq {
    post_id: string
    text: string;
}

export interface SaveBlogPostReq {

    id?: string | null; // undefined. id가 없으면 -> create / id가 있으면 -> update로
    title: string;
    page_json: string;
    page_html?: string | null;

}

// const res = await fetch(`http://localhost:8089/api/blog/posts/${params.id}`, {
//     cache: "no-store", // SSR 목적일 경우
//   }); // 별도의 필드 주입이 없으면 get 사용
//   //console.log("posts : ",await res.json())
//   console.log();
//   const post = await res.json();
//   //const posts = await res.json();


export interface getPostJsonRes {

    id: string;
    title: string;
    page_json: string;

}



export async function saveBlogPost(
    data: SaveBlogPostReq

){
    const res = await jsonApi.post('/api/blog/saveblogpost', data);
    return { status : res.status }

}

export async function updateBlogPost(
    data: SaveBlogPostReq

){
    const res = await jsonApi.put('/api/blog/posts', data);

    return { status : res.status }
}

export async function deletePost(
    id: string
): Promise<number> {

    const res = await jsonApi.delete(`/api/blog/posts/${id}`)

    return res.status;
}

export async function getPostJson(
    id: string
): Promise<getPostJsonRes> {

    const res = await jsonApi.get(`/api/blog/posts/getjson/${id}`);

    return res.data;

}

export async function SaveComment(
    data: SaveCommentReq
): Promise<AxiosResponse> {
    return await jsonApi.post(`/api/blog/comment`, data)
}


export async function deleteComment(
    id: string
): Promise<AxiosResponse> {

    return await jsonApi.delete(`/api/blog/comment/${id}`)
}



export async function getPostCommentsAxios(postId: number): Promise<AxiosResponse> {
    return await jsonApi.get(`/api/blog/comments/${postId}`)
}

기존 axios 서비스 계층을 use server 처리함. 

(장기적으로는 fetch로 이동해야 next에서 제공하는 최적화 등을 받을 수 있음)

이렇게 할 경우 프론트에서 해당 코드를 호출할 경우 자동으로 서버쪽에서 해당 코드가 실행됨.