서버 내부의 요청 라이프사이클
요청이 서버에 도착해서 응답이 반환되기까지의 전체 라이프사이클을 따라가세요.
요청이 서버에 도달하면 무슨 일이 일어나는가
백엔드 서버는 요청을 처리하기 위해 구조화된 파이프라인을 따릅니다—각 단계는 들어오는 요청을 응답으로 변환하는 명확한 역할을 가집니다.
- 요청은 무작위가 아니라 예측 가능한 순서로 처리됩니다.
- 파이프라인의 각 단계는 특정한 책임을 수행합니다.
- 프레임워크는 시스템을 체계적으로 유지하고 관리하기 쉽게 만들기 위해 이 구조를 강제합니다.
상세정보
클라이언트가 서버에 HTTP 요청을 보내면, 시스템은 즉시 비즈니스 로직을 실행하지 않습니다. 대신 요청은 정의된 단계들의 순서를 따라 흐릅니다. 이 파이프라인은 라우팅, 인증, 데이터 처리와 같은 각 관심사를 올바른 순서로 처리되도록 보장합니다.
일반적인 생명주기는 다음과 같습니다:
HTTP 요청
↓
라우터
↓
미들웨어
↓
인증
↓
비즈니스 로직
↓
데이터베이스
↓
HTTP 응답
이 구조는 선택 사항이 아닙니다—현대 백엔드 프레임워크가 이렇게 설계되어 있습니다. Express, Django, Spring Boot, FastAPI를 사용하든, 모든 시스템은 이 파이프라인의 어떤 변형을 구현합니다.
장점은 제어와 예측 가능성입니다. 책임을 섞지 않고 각 단계가 명확한 목적을 가집니다. 엔지니어는 특정 지점에 로직을 삽입할 수 있습니다(예: 비즈니스 로직 전에 인증 추가) 다른 부분에 영향을 주지 않고도 말입니다.
이 파이프라인이 없다면, 백엔드 시스템은 검증, 보안, 비즈니스 로직이 뒤섞여 금방 혼란스러워질 것입니다. 구조화된 흐름은 요청이 일관되고, 안전하며, 효율적으로 처리되도록 보장합니다.
라우팅
라우팅은 들어오는 요청을 URL과 HTTP 메서드에 따라 애플리케이션의 어느 부분이 처리할지 결정합니다.
- 라우터는 URL 경로를 사용해 들어오는 요청을 매칭합니다.
- HTTP 메서드(GET, POST 등)는 요청을 어떻게 처리할지 더 구체적으로 정의합니다.
- 각 라우트는 특정 핸들러 함수 또는 컨트롤러에 매핑됩니다.
상세정보
요청이 서버에 도착하면, 가장 먼저 결정해야 하는 것은 어떤 코드가 이를 처리할지입니다. 이것이 라우터의 역할입니다.
라우터는 요청의 두 가지 핵심 부분인 URL 경로와 HTTP 메서드를 검사합니다. 이 둘을 함께 사용하면 클라이언트가 무엇을 하려는지 고유하게 식별할 수 있습니다.
예를 들어:
GET /users
→ usersController.getUsers
POST /orders
→ ordersController.createOrder
같은 URL을 사용하더라도 HTTP 메서드가 다르면 완전히 다른 로직으로 라우팅될 수 있습니다. 이를 통해 API는 데이터 읽기(GET)와 데이터 생성(POST) 같은 작업을 명확하게 분리할 수 있습니다.
내부적으로 프레임워크는 요청 패턴을 핸들러 함수에 매핑하는 라우팅 테이블을 유지합니다. 요청이 어떤 라우트와 일치하면 해당 핸들러가 실행됩니다.
이 추상화는 시스템을 체계적으로 유지해 줍니다. 모든 요청을 직접 조건문으로 검사하는 대신, 개발자는 라우트를 선언적으로 정의합니다. 프레임워크가 매칭을 효율적으로 처리하므로, 항상 올바른 코드가 실행됩니다.
미들웨어
미들웨어는 파이프라인을 통과하는 요청을 가로채는 함수로, 시스템이 핸들러에 도달하기 전에 교차 관심사 로직을 적용할 수 있게 해줍니다.
- 미들웨어는 주요 요청 핸들러 이전 또는 이후에 실행됩니다.
- 로깅, 인증, 검증 같은 공통 관심사에 사용됩니다.
- 여러 미들웨어 함수를 순차적으로 연결할 수 있습니다.
상세정보
미들웨어는 들어오는 요청과 이를 처리하는 최종 핸들러 사이에 위치합니다. 라우팅에서 비즈니스 로직으로 바로 이동하는 대신, 요청은 하나 이상의 미들웨어 함수를 거칩니다.
흐름은 다음과 같습니다:
요청
↓
미들웨어
↓
핸들러
각 미들웨어 함수는 요청이 다음 단계에 도달하기 전에 이를 검사, 수정, 또는 중단할 수 있습니다. 따라서 미들웨어는 여러 라우트에 공통으로 적용되는 관심사를 처리하는 데 매우 강력합니다.
일반적인 사용 사례로는 들어오는 요청 로깅, 인증 토큰 검증, 요청 데이터 확인, 속도 제한 적용, 오류를 일관된 방식으로 처리하는 것 등이 있습니다.
미들웨어는 또한 체인의 다음 함수로 제어를 넘길 수 있어, 계층형 처리 모델을 만들 수 있습니다. 이렇게 하면 반복적인 작업을 별도로 처리할 수 있으므로 주요 비즈니스 로직이 깔끔하게 유지됩니다.
미들웨어가 없다면 이러한 관심사들을 모든 핸들러 안에 중복해서 작성해야 하므로, 코드가 복잡해지고 유지보수가 어려워집니다. 미들웨어는 이 로직을 중앙화하고 애플리케이션 전반의 일관성을 보장합니다.
인증 단계
인증은 요청을 누가 보내는지 확인하고, 비즈니스 로직이 실행되기 전에 신원 정보를 연결합니다.
- 인증은 토큰이나 세션과 같은 자격 증명을 확인합니다.
- 유효한 요청에는 사용자 신원 정보가 추가됩니다.
- 자격 증명이 없거나 잘못된 경우 요청이 조기에 중단될 수 있습니다.
상세정보
인증은 시스템이 어떤 비즈니스 로직을 수행하기 전에 요청자의 신원을 확인합니다. 이 단계가 없으면 서버는 모든 요청을 익명으로 처리하게 되어, 접근 제어나 사용자별 동작을 적용할 수 없게 됩니다.
이 과정은 일반적으로 토큰, 세션 또는 API 키를 사용해 처리됩니다. 예를 들어, 클라이언트가 요청 헤더에 JWT를 보내면 서버가 이를 검증하거나, 쿠키에 저장된 세션 ID를 포함해 서버의 사용자 레코드와 매핑할 수 있습니다.
자격 증명이 검증되면 시스템은 사용자 ID, 역할, 권한과 같은 신원 정보를 추출합니다. 이 정보는 요청 컨텍스트에 첨부되어, 이후 단계의 컴포넌트들이 사용자가 누구인지에 따라 결정을 내릴 수 있게 합니다.
이 단계는 파이프라인의 초기에 배치됩니다. 시스템의 많은 부분이 이 정보에 의존하기 때문입니다. 비즈니스 로직은 종종 권한을 확인하거나, 사용자별 데이터를 가져오거나, 신원에 따라 제한을 적용해야 합니다.
인증에 실패하면 요청은 보통 401 Unauthorized 응답과 함께 즉시 거부됩니다. 이렇게 초기에 처리하면 권한 없는 접근을 막을 수 있고, 진행되어서는 안 되는 요청에 리소스를 낭비하지 않아도 됩니다.
요청 검증
요청 검증은 들어오는 데이터에 엄격한 규칙을 적용하여, 형식이 올바르고 예상된 입력만 애플리케이션 로직에 도달하도록 합니다.
{
"name": "John",
"age": 25,
"email": "john@example.com"
}거부됨
데이터 구조를 검증하는 중...
검증은 형식이 잘못되었거나 안전하지 않은 데이터가 데이터베이스에 도달하는 것을 방지합니다.
- 서버는 유효한 요청 구조를 설명하는 스키마를 정의합니다.
- 누락되었거나 잘못된 타입의 필드는 즉시 거부됩니다.
- 정제(sanitization)는 유해하거나 안전하지 않은 입력의 위험을 줄입니다.
상세정보
요청 검증은 외부 입력과 내부 로직 사이에서 게이트키퍼 역할을 합니다. 클라이언트가 항상 올바른 데이터를 보내는 것은 아니므로, 서버는 모든 요청에 대해 규칙을 적극적으로 적용해야 합니다.
이러한 규칙은 보통 스키마를 사용해 정의됩니다. 스키마는 어떤 필드가 필수인지, 어떤 데이터 타입이 허용되는지, 그리고 값이 어떤 구조를 가져야 하는지를 지정합니다. 요청이 스키마와 일치하지 않으면, 추가 처리 전에 거부됩니다.
예를 들어, 사용자 생성 요청에는 유효한 형식의 email 필드가 필요할 수 있습니다. 해당 필드가 없거나 형식이 잘못되면, 서버는 잘못된 데이터를 처리하려고 하지 않고 오류를 반환합니다.
구조뿐 아니라, 검증에는 종종 정제(sanitization)도 포함됩니다. 이는 위험한 입력을 제거하거나 이스케이프하여, 인젝션 공격이나 예상치 못한 동작 같은 문제로부터 시스템을 보호합니다.
검증을 초기에 강제함으로써 백엔드 시스템은 일관성을 유지하고, 버그를 줄이며, 잘못된 데이터가 애플리케이션 깊숙이 퍼지는 것을 방지할 수 있습니다.
컨트롤러와 비즈니스 로직
컨트롤러는 요청을 조정하고, 비즈니스 로직은 애플리케이션이 실제로 어떻게 동작하고 데이터를 처리하는지를 정의합니다.
- 컨트롤러는 검증된 요청을 받아 워크플로를 조율합니다.
- 비즈니스 로직은 애플리케이션의 핵심 규칙을 구현합니다.
- 이 계층은 서비스, 데이터베이스, 외부 시스템과 상호작용합니다.
상세정보
요청이 라우팅, 인증, 검증을 통과하면 컨트롤러에 도달합니다. 컨트롤러는 애플리케이션 로직의 진입점 역할을 합니다. 컨트롤러 자체에는 복잡한 로직이 들어 있지 않고, 대신 다음에 무엇을 해야 하는지 조정합니다.
컨트롤러는 비즈니스 로직을 호출하며, 실제 작업은 그곳에서 이루어집니다. 비즈니스 로직은 시스템이 어떻게 동작하는지, 즉 데이터가 어떻게 처리되는지, 어떤 규칙이 적용되는지, 어떤 작업이 수행되는지를 정의합니다.
예를 들어 주문을 생성하는 과정에는 여러 단계가 포함될 수 있습니다. 총 가격 계산, 재고 가능 여부 확인, 그리고 주문을 데이터베이스에 저장하는 단계입니다. 이러한 단계는 컨트롤러 자체가 아니라 비즈니스 로직의 일부입니다.
컨트롤러와 비즈니스 로직을 분리하면 시스템을 깔끔하고 유지보수하기 쉽게 유지할 수 있습니다. 컨트롤러는 요청 흐름을 처리하고, 비즈니스 로직은 애플리케이션 규칙에 집중합니다. 이러한 분리는 다른 부분을 깨뜨리지 않고도 시스템을 더 쉽게 테스트하고, 확장하고, 수정할 수 있게 해줍니다.
데이터베이스 상호작용
데이터 계층은 구조화된 데이터베이스 작업을 통해 영속 데이터를 저장, 조회, 업데이트하는 역할을 합니다.
- 애플리케이션 로직은 데이터를 읽거나 수정하기 위해 쿼리를 실행합니다.
- 데이터베이스는 데이터가 단일 요청을 넘어 계속 유지되도록 영속화합니다.
- 트랜잭션은 복잡한 작업 중 데이터 일관성을 보장합니다.
상세정보
비즈니스 로직이 무엇을 해야 하는지 결정한 후, 시스템은 데이터베이스와 상호작용하여 데이터를 읽거나 씁니다. 이때 영속 상태가 관리됩니다. 메모리 내 데이터와 달리 데이터베이스 데이터는 장기적으로 저장됩니다.
애플리케이션은 정보를 가져오거나 레코드를 업데이트하기 위해 데이터베이스에 쿼리를 보냅니다. 이러한 쿼리는 단순한 조회부터 여러 테이블이나 조건이 포함된 더 복잡한 작업까지 다양할 수 있습니다.
많은 경우 작업은 트랜잭션으로 감싸집니다. 트랜잭션은 변경 사항들의 묶음이 모두 성공하거나 모두 실패하도록 보장합니다. 이는 특히 결제나 주문 처리 같은 작업에서 일관성을 유지하는 데 매우 중요합니다.
데이터베이스가 쿼리를 처리하면 결과를 애플리케이션에 반환합니다. 그런 다음 비즈니스 로직은 이 데이터를 사용해 처리를 계속하거나 클라이언트로 보낼 최종 응답을 준비합니다.
응답 구성하기
서버는 처리된 결과를 클라이언트가 해석하고 사용할 수 있는 표준화된 HTTP 응답으로 변환합니다.
- 데이터는 전송을 위해 JSON 같은 형식으로 직렬화됩니다.
- 상태 코드는 성공, 실패 또는 특정 결과를 나타냅니다.
- 응답은 네트워크를 통해 클라이언트로 다시 전송됩니다.
상세정보
모든 처리가 완료되면, 백엔드는 내부 결과를 클라이언트에 적합한 형식으로 변환해야 합니다. 이 과정에는 보통 HTTP를 통해 전송할 수 있도록 데이터를 JSON으로 직렬화하는 작업이 포함됩니다.
서버는 또한 결과를 전달하기 위해 HTTP 상태 코드를 함께 붙입니다. 이러한 코드는 요청이 성공했는지, 클라이언트 입력 때문에 실패했는지, 또는 서버 측 문제를 만났는지에 대한 즉각적인 맥락을 제공합니다.
예를 들면:
HTTP 200 OK
"userId": 42
이 단계는 응답이 일관된 계약을 따르도록 보장합니다. 클라이언트는 서버 내부를 이해할 필요 없이, 예측 가능한 형식과 상태 코드를 바탕으로 결과를 올바르게 처리할 수 있습니다.
응답이 구성되면 네트워크를 통해 다시 전송됩니다. 이는 해당 요청에 대한 서버의 책임이 끝났음을 의미하며, 전체 요청-응답 사이클이 완료됩니다.
질문 섹션
1 / 5
이 레슨은 프리미엄 콘텐츠입니다
프리미엄으로 업그레이드하여 흐림 효과를 없애고 전체 내용을 읽어 보세요.