티스토리 뷰

반응형

백엔드가 진짜 백엔드처럼 보이기 시작하는 순간 — 공통 응답 포맷과 전역 예외 처리 구현하기 (FastAPI · Spring Boot · Node.js)

백엔드 프로젝트를 처음 만들 때는 보통 이런 데서 기분이 좋아집니다.

“오, API 호출하면 값 잘 오네?”

근데 조금만 기능이 늘어나면 바로 이상한 일이 생겨요.

  • 어떤 API는 { "name": "..." } 로 주고
  • 어떤 API는 { "data": { ... } } 로 주고
  • 어떤 API는 에러가 나면 그냥 문자열만 내려주고
  • 어떤 API는 500인데 왜 실패했는지 감도 안 오고
  • 프론트는 매번 응답 형식이 달라서 분기문이 늘어나고
  • 나중엔 “이건 어느 형식이 맞는 거지?”가 됩니다

저는 이 단계가 백엔드 초반에 진짜 중요하다고 생각해요.
구조를 잘 나눠도, 응답 형식과 에러 처리 규칙이 없으면 API 전체가 금방 산만해집니다.

그래서 이번 글에서는
공통 응답 포맷전역 예외 처리
FastAPI, Spring Boot, Node.js(Express) 세 가지로 똑같은 기준에 맞춰 구현해보겠습니다.

FastAPI는 HTTPException과 커스텀 exception handler, 그리고 RequestValidationError 오버라이드를 공식적으로 지원합니다. (FastAPI)
Spring MVC 쪽은 @ControllerAdvice와 ResponseEntityExceptionHandler, ProblemDetail 기반 응답을 공식 문서에서 안내하고 있습니다. (Home)
Express는 기본 에러 핸들러가 있지만, 실무에서는 4개 인자를 받는 에러 미들웨어 (err, req, res, next)를 직접 두는 방식을 공식 가이드가 설명합니다. Express 5에서는 async handler에서 발생한 rejected promise도 에러 핸들러로 전달됩니다. (Express)


왜 공통 응답 포맷이 필요한가

처음에는 그냥 데이터만 잘 내려가면 된다고 생각하기 쉬워요.
저도 그랬습니다.

근데 프론트엔드, 모바일, 다른 백엔드, 배치, 로그 분석, 모니터링까지 생각하면
응답은 “값만 내려주는 것”이 아니라 약속이더라고요.

예를 들어 아래 두 API를 생각해보죠.

{
  "id": 1,
  "name": "Alice"
}
{
  "success": true,
  "data": {
    "id": 1,
    "name": "Alice"
  },
  "message": "사용자 조회 성공"
}

둘 다 틀린 건 아닙니다.
근데 팀 단위로 가면 두 번째처럼 형식을 맞추는 쪽이 관리가 훨씬 쉽습니다.

저는 보통 성공 응답은 이렇게 맞추는 편이에요.

{
  "success": true,
  "data": { ... },
  "message": "요청 성공"
}

에러 응답은 이렇게 갑니다.

{
  "success": false,
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "사용자를 찾을 수 없습니다"
  }
}

이렇게 해두면 좋은 점이 분명해요.

  • 프론트가 파싱 규칙을 통일할 수 있음
  • 에러 메시지와 에러 코드를 분리할 수 있음
  • 로그나 모니터링에서 에러 코드 단위로 추적 가능
  • 문서화가 쉬워짐
  • 테스트 케이스 작성이 편해짐

이번 글에서 맞출 공통 규칙

이번 글에서는 세 스택 모두 아래 규칙으로 맞추겠습니다.

성공 응답

  • success: true
  • data: 실제 데이터
  • message: 사람이 읽기 쉬운 설명

실패 응답

  • success: false
  • error.code: 에러 코드
  • error.message: 에러 메시지

예외 종류

  • 비즈니스 예외: 예를 들어 사용자를 찾을 수 없음
  • 검증 예외: 요청값이 잘못됨
  • 알 수 없는 예외: 서버 내부 오류

이 규칙을 세 프레임워크에 똑같이 적용해볼 거예요.


1) FastAPI에서 공통 응답 포맷과 전역 예외 처리

FastAPI는 기본적으로 HTTPException을 지원하고, @app.exception_handler()로 커스텀 예외와 RequestValidationError를 전역 처리할 수 있습니다. 공식 문서도 이 패턴을 직접 보여줍니다. (FastAPI)

추천 구조

fastapi-backend/
├── app/
│   ├── api/
│   │   └── user.py
│   ├── common/
│   │   ├── exceptions.py
│   │   ├── handlers.py
│   │   └── responses.py
│   ├── schemas/
│   │   └── user.py
│   ├── services/
│   │   └── user_service.py
│   └── main.py
└── requirements.txt

requirements.txt

fastapi
uvicorn[standard]
pydantic

app/common/responses.py

반응형
from typing import Generic, Optional, TypeVar

from pydantic import BaseModel

T = TypeVar("T")


class ErrorDetail(BaseModel):
    code: str
    message: str


class ApiResponse(BaseModel, Generic[T]):
    success: bool
    message: str
    data: Optional[T] = None
    error: Optional[ErrorDetail] = None


def success_response(data: T, message: str = "요청 성공") -> ApiResponse[T]:
    return ApiResponse[T](
        success=True,
        message=message,
        data=data,
        error=None,
    )


def error_response(code: str, message: str) -> ApiResponse[None]:
    return ApiResponse[None](
        success=False,
        message=message,
        data=None,
        error=ErrorDetail(code=code, message=message),
    )

app/common/exceptions.py

class AppException(Exception):
    def __init__(self, code: str, message: str, status_code: int = 400):
        self.code = code
        self.message = message
        self.status_code = status_code
        super().__init__(message)


class UserNotFoundException(AppException):
    def __init__(self, user_id: int):
        super().__init__(
            code="USER_NOT_FOUND",
            message=f"사용자({user_id})를 찾을 수 없습니다.",
            status_code=404,
        )

app/common/handlers.py

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

from app.common.exceptions import AppException
from app.common.responses import error_response


def register_exception_handlers(app: FastAPI) -> None:
    @app.exception_handler(AppException)
    async def handle_app_exception(request: Request, exc: AppException):
        return JSONResponse(
            status_code=exc.status_code,
            content=error_response(exc.code, exc.message).model_dump(),
        )

    @app.exception_handler(RequestValidationError)
    async def handle_validation_exception(request: Request, exc: RequestValidationError):
        return JSONResponse(
            status_code=422,
            content=error_response(
                "VALIDATION_ERROR",
                "요청값이 올바르지 않습니다.",
            ).model_dump(),
        )

    @app.exception_handler(Exception)
    async def handle_unexpected_exception(request: Request, exc: Exception):
        return JSONResponse(
            status_code=500,
            content=error_response(
                "INTERNAL_SERVER_ERROR",
                "서버 내부 오류가 발생했습니다.",
            ).model_dump(),
        )

app/schemas/user.py

from pydantic import BaseModel, Field


class UserResponse(BaseModel):
    id: int
    name: str


class UserQuery(BaseModel):
    user_id: int = Field(gt=0)

app/services/user_service.py

from app.common.exceptions import UserNotFoundException
from app.schemas.user import UserResponse


class UserService:
    @staticmethod
    def get_user(user_id: int) -> UserResponse:
        fake_users = {
            1: {"id": 1, "name": "Alice"},
            2: {"id": 2, "name": "Bob"},
        }

        user = fake_users.get(user_id)
        if user is None:
            raise UserNotFoundException(user_id)

        return UserResponse(**user)

app/api/user.py

from fastapi import APIRouter, Query

from app.common.responses import ApiResponse, success_response
from app.schemas.user import UserResponse
from app.services.user_service import UserService

router = APIRouter(prefix="/api/users", tags=["users"])


@router.get("/{user_id}", response_model=ApiResponse[UserResponse])
def get_user(user_id: int):
    user = UserService.get_user(user_id)
    return success_response(user, "사용자 조회 성공")

app/main.py

from fastapi import FastAPI

from app.api.user import router as user_router
from app.common.handlers import register_exception_handlers

app = FastAPI(title="backend-series-fastapi")

register_exception_handlers(app)
app.include_router(user_router)

실행

uvicorn app.main:app --reload

성공 테스트

curl http://127.0.0.1:8000/api/users/1

응답:

{
  "success": true,
  "message": "사용자 조회 성공",
  "data": {
    "id": 1,
    "name": "Alice"
  },
  "error": null
}

비즈니스 예외 테스트

curl http://127.0.0.1:8000/api/users/999

응답:

{
  "success": false,
  "message": "사용자(999)를 찾을 수 없습니다.",
  "data": null,
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "사용자(999)를 찾을 수 없습니다."
  }
}

FastAPI에서 기억할 포인트

FastAPI는 response_model로 응답 구조를 문서화하고 검증할 수 있고, 전역 예외 핸들러에서 공통 포맷을 강제하기 좋습니다. 공식 문서도 validation 예외와 커스텀 예외 오버라이드를 명시적으로 지원합니다. (FastAPI)

제가 느끼는 FastAPI의 장점은 이거예요.
응답 포맷과 문서화가 아주 가깝게 붙어 있습니다.
그래서 초반에 규칙을 잘 잡아두면 Swagger/OpenAPI 화면도 꽤 깔끔해져요.


2) Spring Boot에서 공통 응답 포맷과 전역 예외 처리

Spring MVC는 @ExceptionHandler, @ControllerAdvice, ResponseEntityExceptionHandler를 통해 예외를 전역 처리할 수 있고, 최근 문서에서는 ProblemDetail 기반 에러 응답도 강조합니다. (Home)

여기서는 시리즈 흐름상, 프론트와 맞추기 쉬운 커스텀 공통 응답 포맷으로 먼저 가겠습니다.

추천 구조

springboot-backend/
├── src/main/java/com/example/backend/
│   ├── BackendApplication.java
│   ├── controller/
│   │   └── UserController.java
│   ├── service/
│   │   └── UserService.java
│   ├── dto/
│   │   ├── ApiResponse.java
│   │   ├── ErrorDetail.java
│   │   └── UserResponse.java
│   ├── exception/
│   │   ├── AppException.java
│   │   ├── UserNotFoundException.java
│   │   └── GlobalExceptionHandler.java
│   └── common/
│       └── ApiResponses.java
└── build.gradle

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '4.0.3'
    id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

dto/ErrorDetail.java

package com.example.backend.dto;

public record ErrorDetail(
        String code,
        String message
) {
}

dto/ApiResponse.java

package com.example.backend.dto;

public record ApiResponse<T>(
        boolean success,
        String message,
        T data,
        ErrorDetail error
) {
}

dto/UserResponse.java

package com.example.backend.dto;

public record UserResponse(
        Long id,
        String name
) {
}

common/ApiResponses.java

package com.example.backend.common;

import com.example.backend.dto.ApiResponse;
import com.example.backend.dto.ErrorDetail;

public final class ApiResponses {

    private ApiResponses() {
    }

    public static <T> ApiResponse<T> success(T data, String message) {
        return new ApiResponse<>(true, message, data, null);
    }

    public static ApiResponse<Void> error(String code, String message) {
        return new ApiResponse<>(false, message, null, new ErrorDetail(code, message));
    }
}

exception/AppException.java

package com.example.backend.exception;

public class AppException extends RuntimeException {
    private final String code;
    private final int status;

    public AppException(String code, String message, int status) {
        super(message);
        this.code = code;
        this.status = status;
    }

    public String getCode() {
        return code;
    }

    public int getStatus() {
        return status;
    }
}

exception/UserNotFoundException.java

package com.example.backend.exception;

public class UserNotFoundException extends AppException {
    public UserNotFoundException(Long userId) {
        super(
                "USER_NOT_FOUND",
                "사용자(" + userId + ")를 찾을 수 없습니다.",
                404
        );
    }
}

service/UserService.java

package com.example.backend.service;

import com.example.backend.dto.UserResponse;
import com.example.backend.exception.UserNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class UserService {

    private final Map<Long, UserResponse> fakeUsers = Map.of(
            1L, new UserResponse(1L, "Alice"),
            2L, new UserResponse(2L, "Bob")
    );

    public UserResponse getUser(Long userId) {
        UserResponse user = fakeUsers.get(userId);

        if (user == null) {
            throw new UserNotFoundException(userId);
        }

        return user;
    }
}

controller/UserController.java

package com.example.backend.controller;

import com.example.backend.common.ApiResponses;
import com.example.backend.dto.ApiResponse;
import com.example.backend.dto.UserResponse;
import com.example.backend.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/api/users/{userId}")
    public ApiResponse<UserResponse> getUser(@PathVariable Long userId) {
        return ApiResponses.success(
                userService.getUser(userId),
                "사용자 조회 성공"
        );
    }
}

exception/GlobalExceptionHandler.java

package com.example.backend.exception;

import com.example.backend.common.ApiResponses;
import com.example.backend.dto.ApiResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(AppException.class)
    public ResponseEntity<ApiResponse<Void>> handleAppException(AppException e) {
        return ResponseEntity
                .status(e.getStatus())
                .body(ApiResponses.error(e.getCode(), e.getMessage()));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResponse<Void>> handleUnexpectedException(Exception e) {
        return ResponseEntity
                .status(500)
                .body(ApiResponses.error(
                        "INTERNAL_SERVER_ERROR",
                        "서버 내부 오류가 발생했습니다."
                ));
    }
}

실행

./gradlew bootRun

성공 테스트

curl http://127.0.0.1:8080/api/users/1

응답:

{
  "success": true,
  "message": "사용자 조회 성공",
  "data": {
    "id": 1,
    "name": "Alice"
  },
  "error": null
}

에러 테스트

curl http://127.0.0.1:8080/api/users/999

응답:

{
  "success": false,
  "message": "사용자(999)를 찾을 수 없습니다.",
  "data": null,
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "사용자(999)를 찾을 수 없습니다."
  }
}

Spring Boot에서 기억할 포인트

Spring은 원래 예외 처리 도구가 굉장히 잘 갖춰져 있습니다. @ControllerAdvice와 @ExceptionHandler는 예외를 한 군데로 모으는 정석적인 방법이고, 최근 공식 문서는 RFC 9457 스타일의 ProblemDetail도 강하게 지원합니다. (Home)

근데 실무에서는 종종 이런 고민이 생겨요.

“ProblemDetail 표준으로 갈까, 우리 공통 응답 포맷으로 갈까?”

제 경험상

  • 외부 공개 API나 표준 지향이면 ProblemDetail
  • 내부 프론트/앱과 강하게 맞물린 서비스면 커스텀 공통 포맷

이렇게 가는 경우가 많았습니다.
이번 글은 시리즈 통일성을 위해 커스텀 포맷으로 맞췄고, 나중에 한 편 따로 ProblemDetail도 다뤄볼 만합니다.


3) Node.js(Express)에서 공통 응답 포맷과 전역 예외 처리

Express는 기본 에러 핸들러가 있지만, 공식 가이드는 에러 처리 미들웨어를 직접 두는 방식을 안내합니다. 에러 미들웨어는 반드시 (err, req, res, next) 시그니처를 가져야 하고, Express 5에서는 async handler의 throw/rejection도 자동으로 에러 핸들러로 전달됩니다. (Express)

추천 구조

node-backend/
├── src/
│   ├── common/
│   │   ├── app-error.js
│   │   ├── error-handler.js
│   │   └── responses.js
│   ├── routes/
│   │   └── user.route.js
│   ├── services/
│   │   └── user.service.js
│   └── server.js
└── package.json

package.json

{
  "name": "backend-series-node",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "node src/server.js"
  },
  "dependencies": {
    "express": "^5.1.0"
  }
}

src/common/responses.js

export function successResponse(data, message = "요청 성공") {
  return {
    success: true,
    message,
    data,
    error: null,
  };
}

export function errorResponse(code, message) {
  return {
    success: false,
    message,
    data: null,
    error: {
      code,
      message,
    },
  };
}

src/common/app-error.js

export class AppError extends Error {
  constructor(code, message, status = 400) {
    super(message);
    this.code = code;
    this.status = status;
  }
}

export class UserNotFoundError extends AppError {
  constructor(userId) {
    super(
      "USER_NOT_FOUND",
      `사용자(${userId})를 찾을 수 없습니다.`,
      404
    );
  }
}

src/common/error-handler.js

import { errorResponse } from "./responses.js";

export function errorHandler(err, req, res, next) {
  if (err.code && err.status) {
    return res.status(err.status).json(errorResponse(err.code, err.message));
  }

  console.error(err);

  return res
    .status(500)
    .json(errorResponse("INTERNAL_SERVER_ERROR", "서버 내부 오류가 발생했습니다."));
}

src/services/user.service.js

import { UserNotFoundError } from "../common/app-error.js";

const fakeUsers = new Map([
  [1, { id: 1, name: "Alice" }],
  [2, { id: 2, name: "Bob" }],
]);

export function getUser(userId) {
  const user = fakeUsers.get(userId);

  if (!user) {
    throw new UserNotFoundError(userId);
  }

  return user;
}

src/routes/user.route.js

import { Router } from "express";
import { successResponse } from "../common/responses.js";
import { getUser } from "../services/user.service.js";

const router = Router();

router.get("/api/users/:userId", async (req, res) => {
  const userId = Number(req.params.userId);

  if (Number.isNaN(userId) || userId <= 0) {
    return res.status(400).json({
      success: false,
      message: "요청값이 올바르지 않습니다.",
      data: null,
      error: {
        code: "VALIDATION_ERROR",
        message: "요청값이 올바르지 않습니다.",
      },
    });
  }

  const user = getUser(userId);
  return res.json(successResponse(user, "사용자 조회 성공"));
});

export default router;

src/server.js

import express from "express";
import userRouter from "./routes/user.route.js";
import { errorHandler } from "./common/error-handler.js";

const app = express();
const PORT = 3000;

app.use(express.json());
app.use(userRouter);
app.use(errorHandler);

app.listen(PORT, () => {
  console.log(`server running on http://localhost:${PORT}`);
});

실행

npm install
npm run dev

성공 테스트

curl http://127.0.0.1:3000/api/users/1

응답:

{
  "success": true,
  "message": "사용자 조회 성공",
  "data": {
    "id": 1,
    "name": "Alice"
  },
  "error": null
}

에러 테스트

curl http://127.0.0.1:3000/api/users/999

응답:

{
  "success": false,
  "message": "사용자(999)를 찾을 수 없습니다.",
  "data": null,
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "사용자(999)를 찾을 수 없습니다."
  }
}

Node.js에서 기억할 포인트

Express는 정말 얇습니다.
그게 장점이기도 한데, 그 말은 곧 에러 규칙도 직접 잡아야 한다는 뜻이기도 해요.

공식 가이드가 말하는 핵심도 딱 그겁니다.

  • 기본 핸들러는 있지만 실무용으론 직접 두는 게 낫다
  • 에러 핸들러는 4개 인자 시그니처여야 한다
  • Express 5에서는 async 함수에서 throw된 에러도 핸들러로 넘어간다 (Express)

개인적으로 Node.js에서는
throw new Error("...")를 여기저기서 막 쓰기 시작하면
나중에 에러 코드 추적이 진짜 힘들어집니다.
그래서 AppError 같은 공통 에러 클래스를 초반에 두는 게 꽤 중요해요.


세 스택을 나란히 놓고 보면

이 세 가지는 결국 같은 목표를 향합니다.

FastAPI

  • 타입 기반 응답 모델 정의가 쉬움
  • validation 예외와 커스텀 예외 오버라이드가 자연스러움
  • Swagger/OpenAPI와 연결이 좋음 (FastAPI)

Spring Boot

  • 전역 예외 처리 구조가 아주 안정적임
  • @ControllerAdvice 패턴이 강력함
  • 필요하면 ProblemDetail로 표준화하기도 좋음 (Home)

Node.js(Express)

  • 제일 자유롭지만 규칙을 직접 세워야 함
  • 미들웨어 패턴이 명확함
  • Express 5에서 async 에러 전파가 좋아짐 (Express)

실무에서 한 번 더 생각해볼 것

이건 진짜 경험담인데요.
공통 응답 포맷을 만들었다고 끝이 아닙니다.
오히려 그다음부터가 시작이에요.

제가 보통 같이 고민하는 건 이런 것들입니다.

1. 에러 코드를 문자열로 관리할까 enum으로 관리할까

초반엔 문자열로 시작해도 되는데, 프로젝트가 커지면 enum 또는 상수 모음으로 빼는 게 좋습니다.

예:

  • USER_NOT_FOUND
  • INVALID_TOKEN
  • FORBIDDEN
  • VALIDATION_ERROR
  • INTERNAL_SERVER_ERROR

2. 사용자에게 보여줄 메시지와 내부 로그 메시지를 분리할까

이건 꽤 중요해요.
사용자에게는 “서버 내부 오류가 발생했습니다.”만 보여주고,
로그에는 실제 스택 트레이스를 남기는 방식이 보통 더 안전합니다.

3. validation 에러 상세를 얼마나 노출할까

FastAPI는 기본 validation 정보가 꽤 자세하고, Spring/Node도 자세히 내려줄 수 있습니다.
근데 외부 공개 API라면 너무 많은 내부 정보를 노출하지 않는 편이 안전한 경우가 많아요. (FastAPI)

4. HTTP status와 business code를 분리해서 생각하자

이거 진짜 자주 헷갈립니다.

  • HTTP 404는 “리소스를 못 찾음”
  • USER_NOT_FOUND는 “우리 도메인에서 사용자를 못 찾음”

둘은 비슷하지만 역할이 다릅니다.
HTTP status는 전송 레벨, 에러 코드는 비즈니스 레벨이라고 생각하면 편해요.


이번 글 핵심 정리

이번 글의 핵심은 하나입니다.

API는 데이터만 주고받는 게 아니라, 실패하는 방식까지 설계해야 한다.

좋은 백엔드는 성공 응답만 깔끔한 게 아니라
실패 응답도 예측 가능해야 합니다.

이번 글에서 만든 기준을 다시 요약하면 이렇습니다.

  • 성공 응답은 success, message, data
  • 실패 응답은 success, message, error.code, error.message
  • 비즈니스 예외는 커스텀 예외로 분리
  • validation 예외와 알 수 없는 예외는 전역 처리
  • 프레임워크가 달라도 응답 규칙은 통일 가능

이 단계까지 오면 API를 보는 사람이 느끼는 인상이 확실히 달라져요.
“아, 이건 그냥 만든 API가 아니라 운영을 생각하고 있구나” 하는 느낌이 납니다.


다음 글 예고

다음 글에서는 이제 진짜 많이 쓰는 주제로 넘어가겠습니다.

로깅과 요청 추적(request id / trace id) 구조 잡기를 해보겠습니다.

이게 들어가면

  • 어떤 요청에서 에러가 났는지
  • 어느 사용자의 요청인지
  • 여러 로그를 어떻게 한 줄로 묶어 추적할지

이런 게 보이기 시작합니다.

솔직히 예외 처리까지 해놨는데 로그가 없으면,
문제 생겼을 때 여전히 깜깜합니다.
그래서 다음 순서로 로깅이 딱 좋습니다.


출처

  • FastAPI 공식 문서 — Handling Errors (FastAPI)
  • FastAPI 공식 문서 — Exceptions Reference (FastAPI)
  • FastAPI 공식 문서 — Response Model (FastAPI)
  • Spring Framework 공식 문서 — Error Responses / REST Exceptions (Home)
  • Spring Framework 공식 문서 — @ExceptionHandler / Exception Handling (Home)
  • Spring Framework Javadoc — ResponseEntity.of(ProblemDetail) (Home)
  • Spring Boot 공식 문서 — Servlet Error Handling (Home)
  • Express 공식 문서 — Error Handling (Express)
  • Express 공식 문서 — Using Middleware / Error-handling Middleware (Express)
  • Express 공식 문서 — Migrating to Express 5 (Express)


백엔드개발, FastAPI, SpringBoot, Nodejs, Express, 전역예외처리, 공통응답포맷, API응답설계, ExceptionHandler, BackendArchitecture

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함
반응형