← 목록으로

프론트엔드와 백엔드 역할 나누기

웹 서비스 개발에서 프론트엔드와 백엔드가 어떤 역할을 하는지, 두 영역의 차이를 설명합니다.

프론트엔드와 백엔드의 기본 역할

프론트엔드는 사용자가 브라우저에서 직접 보고 조작하는 모든 것입니다. 레이아웃, 버튼, 애니메이션, 폼 등이 해당됩니다. HTML, CSS, JavaScript(React, Vue 등)가 사용됩니다.

백엔드는 서버에서 동작하는 모든 것입니다. 데이터 저장, 인증 처리, 비즈니스 로직, 외부 API 연동이 해당됩니다. 사용자는 백엔드를 직접 볼 수 없습니다.

이 두 영역은 API를 통해 통신합니다. 프론트엔드가 "이 사용자의 게시글 목록을 주세요"라고 요청하면, 백엔드가 데이터베이스에서 꺼내 JSON으로 응답하는 방식입니다.


SPA vs SSR vs SSG 비교

SPA (Single Page Application)

브라우저에서 JavaScript가 페이지를 동적으로 렌더링합니다. 첫 로딩 후에는 페이지 전환이 빠르고 앱처럼 동작합니다. Create React App이 대표적입니다.

단점은 초기 로딩이 느리고 SEO에 불리합니다. 검색 엔진이 JavaScript를 실행하기 전의 빈 HTML만 보기 때문입니다.

적합한 경우: 로그인 후에만 접근 가능한 대시보드, 관리자 페이지

SSR (Server-Side Rendering)

서버에서 HTML을 완성해서 브라우저로 보냅니다. 사용자는 완성된 페이지를 즉시 볼 수 있고, 검색 엔진도 콘텐츠를 정확히 읽을 수 있습니다. Next.js의 기본 방식입니다.

단점은 요청마다 서버에서 처리가 필요하므로 서버 부담이 있습니다.

적합한 경우: SEO가 중요한 콘텐츠 페이지, 로그인한 사용자마다 다른 데이터를 보여주는 페이지

SSG (Static Site Generation)

빌드 시점에 HTML을 미리 생성합니다. CDN에서 정적 파일로 서빙하기 때문에 가장 빠릅니다. 블로그, 문서 사이트에 적합합니다.

단점은 데이터가 자주 변하는 서비스에는 적합하지 않습니다. 콘텐츠가 바뀔 때마다 다시 빌드해야 합니다.

적합한 경우: 블로그, 마케팅 랜딩 페이지, 문서 사이트


REST API vs GraphQL vs tRPC 비교

REST API

URL과 HTTP 메서드로 자원을 표현하는 방식입니다. /users/123 GET 요청으로 사용자 정보를 가져오는 방식이 대표적입니다. 이해하기 쉽고 가장 보편적입니다.

단점은 오버페칭(필요 이상의 데이터를 받음)과 언더페칭(여러 번 요청해야 필요한 데이터를 모을 수 있음)이 발생할 수 있습니다.

GraphQL

클라이언트가 필요한 데이터의 구조를 직접 쿼리로 정의합니다. 오버페칭 문제를 해결하고 한 번의 요청으로 여러 리소스를 가져올 수 있습니다. GitHub API, Shopify API가 GraphQL을 제공합니다.

단점은 학습 곡선이 있고, 캐싱 설정이 복잡합니다. 소규모 프로젝트에는 과도한 설계일 수 있습니다.

tRPC

TypeScript 풀스택 프로젝트에서 REST나 GraphQL 없이 타입 안전한 API를 만드는 방법입니다. 서버 함수를 클라이언트에서 마치 로컬 함수처럼 호출합니다. Next.js + tRPC + Prisma 조합이 최근 인기를 얻고 있습니다.

단점은 TypeScript를 사용하지 않거나, 외부 클라이언트(모바일 앱 등)와 API를 공유해야 할 때는 적합하지 않습니다.


BFF(Backend for Frontend) 패턴

BFF는 프론트엔드를 위한 전용 백엔드 레이어를 두는 패턴입니다. 여러 마이크로서비스의 데이터를 조합하고 프론트엔드에 최적화된 형태로 가공하는 역할을 합니다.

예를 들어, 상품 상세 페이지를 렌더링하려면 상품 서비스, 리뷰 서비스, 재고 서비스에 각각 요청해야 합니다. BFF는 이 세 요청을 서버에서 병렬로 처리하고 프론트엔드에 하나의 응답으로 합쳐서 전달합니다.

소규모 프로젝트에서는 BFF가 과도한 설계일 수 있습니다. Next.js의 API Routes나 Server Actions가 사실상 BFF 역할을 합니다.


모놀리스 vs 마이크로서비스

모놀리스

모든 기능이 하나의 애플리케이션에 있는 구조입니다. 개발, 배포, 디버깅이 단순합니다. 초기 스타트업의 올바른 선택입니다.

마이크로서비스

기능별로 독립된 서비스로 분리하는 구조입니다. 각 서비스를 독립적으로 배포하고 확장할 수 있습니다. Netflix, Uber 같은 대형 서비스가 사용합니다.

초기 스타트업에서는 모놀리스로 시작합니다. 마이크로서비스는 팀 규모, 서비스 복잡도, 트래픽 규모가 충분히 커졌을 때 고려합니다. 이른 분리는 오히려 개발 속도를 늦춥니다.


CORS란 무엇이고 어떻게 해결하나

CORS(Cross-Origin Resource Sharing)는 브라우저의 보안 정책으로, 다른 도메인에서 온 요청을 기본적으로 차단합니다.

예를 들어 https://myapp.com에서 실행 중인 프론트엔드가 https://api.myserver.com으로 요청을 보낼 때 CORS 에러가 발생할 수 있습니다.

해결 방법:

서버에서 응답 헤더에 허용할 오리진을 명시합니다.

// Express 예시
import cors from 'cors'

app.use(cors({
  origin: 'https://myapp.com',
  credentials: true
}))

개발 환경에서는 Next.js의 rewrites 설정으로 프록시를 사용하면 CORS 문제를 피할 수 있습니다.


API 인증 방법

JWT (JSON Web Token)

서버가 로그인 시 토큰을 발급하고, 이후 요청에서 이 토큰을 헤더에 포함합니다. 서버가 별도 저장소 없이 토큰 자체에서 사용자 정보를 확인할 수 있습니다. Stateless 구조에 적합합니다.

단점은 토큰을 강제로 만료시키기 어렵습니다(로그아웃 처리 복잡).

Session 기반 인증

로그인 시 서버가 세션을 생성하고 세션 ID를 쿠키에 저장합니다. 이후 요청마다 쿠키의 세션 ID로 사용자를 식별합니다. 서버가 세션을 관리하므로 강제 로그아웃이 쉽습니다.

OAuth

Google, GitHub 등 외부 서비스의 인증을 위임하는 방식입니다. "구글로 로그인"이 OAuth 2.0을 사용합니다. 구현이 복잡하지만, Supabase Auth나 NextAuth.js를 사용하면 쉽게 구현할 수 있습니다.


프론트엔드-백엔드 협업 방법

API 문서화

API를 설계하면 문서를 먼저 작성하고 프론트엔드와 공유합니다. Swagger(OpenAPI)를 사용하면 코드에서 자동으로 문서를 생성할 수 있습니다. Notion으로 간단한 API 명세서를 작성하는 것도 효과적입니다.

Mock API

백엔드가 완성되기 전에 프론트엔드 개발을 시작하고 싶다면, MSW(Mock Service Worker)로 브라우저 수준에서 API를 가상으로 구현합니다. 실제 API와 동일한 인터페이스로 개발해두면 나중에 교체가 쉽습니다.

타입을 공유하는 것도 중요합니다. tRPC나 OpenAPI를 통해 타입을 자동 생성하면 API 스펙 변경 시 프론트엔드에서 즉시 타입 에러로 감지할 수 있습니다.