TIL - 20260528

juni·2026년 5월 28일

TIL

목록 보기
364/468

0528 풀스택 실무 기초 (2/N): REST API 설계와 요청/응답 구조


✅ 1. REST API란 무엇인가?

  • REST API는 클라이언트와 서버가 HTTP를 기반으로 데이터를 주고받기 위한 API 설계 방식입니다.
  • REST는 Representational State Transfer의 약자입니다.
  • 쉽게 말하면, 서버의 데이터를 URL로 표현하고 HTTP 메서드로 작업을 구분하는 방식입니다.

➕ 1-1. API란?

  • API(Application Programming Interface)는 서로 다른 프로그램이 데이터를 주고받기 위한 약속입니다.
  • 프론트엔드 React 앱은 백엔드 NestJS 서버에 API를 호출해서 데이터를 가져오거나 저장합니다.
React 화면
  ↓ API 요청
NestJS 서버
  ↓ DB 조회/저장
Database
  ↓ 결과 반환
NestJS 서버
  ↓ API 응답
React 화면
  • 예를 들어 사용자가 상담 신청을 하면 React는 백엔드에 다음과 같은 API 요청을 보낼 수 있습니다.
POST /api/consults
Content-Type: application/json

{
  "name": "홍길동",
  "phone": "01012345678",
  "model": "Galaxy S25"
}

✅ 2. REST API의 핵심 개념

➕ 2-1. 리소스(Resource)

  • REST API에서는 서버가 관리하는 데이터를 리소스라고 부릅니다.
  • 리소스는 보통 URL 경로로 표현합니다.
리소스URL 예시
사용자/users
상품/products
주문/orders
상담 신청/consults
이벤트/events
배너/banners
  • URL은 “무엇을 다루는지”를 표현해야 합니다.
  • 동작을 URL에 넣기보다는 HTTP 메서드로 구분하는 것이 좋습니다.
좋은 예시:
GET /api/products
POST /api/products
PATCH /api/products/10
DELETE /api/products/10

아쉬운 예시:
GET /api/getProducts
POST /api/createProduct
POST /api/updateProduct
POST /api/deleteProduct

➕ 2-2. HTTP 메서드로 작업 구분하기

  • REST API에서는 같은 URL이라도 HTTP 메서드에 따라 의미가 달라집니다.
메서드의미예시
GET조회상품 목록 조회
POST생성새 상품 등록
PUT전체 수정상품 전체 정보 교체
PATCH부분 수정상품 가격만 수정
DELETE삭제상품 삭제
GET /api/products
  • 상품 목록을 조회합니다.
POST /api/products
  • 새 상품을 등록합니다.
PATCH /api/products/10
  • ID가 10번인 상품의 일부 정보를 수정합니다.
DELETE /api/products/10
  • ID가 10번인 상품을 삭제합니다.

✅ 3. URL 설계 규칙

  • API URL은 프론트엔드와 백엔드가 함께 사용하는 계약입니다.
  • 한 번 배포된 API URL은 쉽게 바꾸기 어렵기 때문에 처음부터 일관성 있게 설계해야 합니다.

➕ 3-1. 명사는 복수형으로 작성

  • REST API에서는 리소스 이름을 보통 복수형으로 작성합니다.
/users
/products
/orders
/consults
/events
  • 하나의 리소스를 조회할 때는 ID를 붙입니다.
/users/1
/products/10
/orders/100
/consults/50

➕ 3-2. URL에는 동사보다 명사를 사용

  • URL에는 create, update, delete, get 같은 동사를 넣기보다 HTTP 메서드로 작업을 표현합니다.
좋은 예시:
GET /api/consults
POST /api/consults
PATCH /api/consults/10
DELETE /api/consults/10

아쉬운 예시:
GET /api/getConsults
POST /api/createConsult
POST /api/updateConsult
POST /api/deleteConsult

➕ 3-3. 계층 구조는 필요한 만큼만 사용

  • 리소스 간 관계가 있을 때는 URL에 계층 구조를 사용할 수 있습니다.
GET /api/users/1/orders
GET /api/events/3/banners
GET /api/products/10/reviews
  • 하지만 계층이 너무 깊어지면 API가 복잡해집니다.
너무 깊은 예시:
GET /api/users/1/orders/5/products/10/options/3
  • 실무에서는 너무 깊은 URL보다 쿼리 파라미터를 활용하는 것이 더 관리하기 쉬운 경우도 많습니다.
GET /api/order-items?userId=1&orderId=5

✅ 4. 쿼리 파라미터와 Path Parameter

➕ 4-1. Path Parameter

  • Path Parameter는 특정 리소스를 식별할 때 사용합니다.
  • URL 경로 안에 ID가 들어가는 형태입니다.
GET /api/products/10
  • 위 요청은 ID가 10번인 상품을 조회한다는 뜻입니다.
@Get(':id')
findOne(@Param('id') id: string) {
  return this.productsService.findOne(Number(id));
}

➕ 4-2. Query Parameter

  • Query Parameter는 조회 조건, 검색어, 정렬, 페이지네이션 등에 사용합니다.
  • URL 뒤에 ?key=value 형태로 붙습니다.
GET /api/products?page=1&limit=20&keyword=galaxy&sort=latest
파라미터의미
page=11페이지
limit=2020개씩 조회
keyword=galaxygalaxy 검색
sort=latest최신순 정렬
@Get()
findAll(
  @Query('page') page: string,
  @Query('limit') limit: string,
  @Query('keyword') keyword: string,
) {
  return this.productsService.findAll({
    page: Number(page),
    limit: Number(limit),
    keyword,
  });
}

✅ 5. 요청 데이터 설계

  • API 요청 데이터는 서버가 처리하기 쉽게 명확한 구조로 보내야 합니다.
  • 프론트엔드와 백엔드가 요청 필드 이름, 타입, 필수 여부를 맞춰야 합니다.

➕ 5-1. 상담 신청 API 요청 예시

POST /api/consults
Content-Type: application/json

{
  "name": "홍길동",
  "phone": "01012345678",
  "model": "Galaxy S25",
  "telecom": "LG U+",
  "agreeMarketing": true
}

➕ 5-2. 요청 데이터 설계 시 확인할 것

  1. 필수값과 선택값이 구분되어 있는가?
  2. 문자열, 숫자, boolean 타입이 명확한가?
  3. 프론트엔드 필드명과 백엔드 DTO 필드명이 일치하는가?
  4. 개인정보가 포함되는가?
  5. 서버에서 검증해야 하는 값인가?
  6. 사용자가 조작하면 안 되는 값이 포함되어 있지 않은가?
  • 특히 가격, 권한, 상태값처럼 중요한 값은 프론트엔드에서 보냈다고 그대로 믿으면 안 됩니다.
  • 서버에서 반드시 다시 계산하거나 검증해야 합니다.

✅ 6. 응답 데이터 설계

  • API 응답 구조가 일정해야 프론트엔드에서 처리하기 쉽습니다.
  • 성공 응답과 실패 응답의 형식이 제각각이면 화면마다 예외 처리가 늘어나고 유지보수가 어려워집니다.

➕ 6-1. 성공 응답 예시

{
  "success": true,
  "message": "상담 신청이 완료되었습니다.",
  "data": {
    "id": 101,
    "createdAt": "2026-05-28T10:30:00.000Z"
  }
}

➕ 6-2. 목록 응답 예시

{
  "success": true,
  "message": "상품 목록 조회에 성공했습니다.",
  "data": {
    "items": [
      {
        "id": 1,
        "name": "Galaxy S25",
        "price": 1200000
      },
      {
        "id": 2,
        "name": "iPhone 16",
        "price": 1300000
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "totalCount": 52,
      "totalPages": 3
    }
  }
}

➕ 6-3. 실패 응답 예시

{
  "success": false,
  "message": "전화번호 형식이 올바르지 않습니다.",
  "errorCode": "INVALID_PHONE_NUMBER",
  "statusCode": 400
}
  • 좋은 실패 응답의 조건

    • 사용자가 이해할 수 있는 메시지가 있어야 합니다.
    • 개발자가 디버깅할 수 있는 에러 코드가 있어야 합니다.
    • HTTP 상태 코드와 응답 내용이 맞아야 합니다.
    • 민감한 서버 내부 정보는 노출하지 않아야 합니다.

✅ 7. DTO와 유효성 검증

  • DTO(Data Transfer Object)는 클라이언트와 서버 사이에서 주고받는 데이터 구조를 정의한 객체입니다.
  • NestJS에서는 DTO와 class-validator를 함께 사용해 요청 데이터를 검증할 수 있습니다.

➕ 7-1. DTO가 필요한 이유

  • 요청 데이터 구조를 명확히 정의할 수 있습니다.
  • 필수값 누락을 막을 수 있습니다.
  • 타입과 형식을 검증할 수 있습니다.
  • Controller 코드가 깔끔해집니다.
  • API 문서화에도 도움이 됩니다.

➕ 7-2. NestJS DTO 예시

import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';

export class CreateConsultDto {
  @IsString()
  @IsNotEmpty()
  name: string;

  @IsString()
  @IsNotEmpty()
  phone: string;

  @IsString()
  @IsNotEmpty()
  model: string;

  @IsString()
  @IsOptional()
  telecom?: string;

  @IsBoolean()
  agreeMarketing: boolean;
}

➕ 7-3. Controller 적용 예시

import { Body, Controller, Post } from '@nestjs/common';
import { CreateConsultDto } from './dto/create-consult.dto';

@Controller('consults')
export class ConsultsController {
  @Post()
  create(@Body() dto: CreateConsultDto) {
    return {
      success: true,
      message: '상담 신청이 완료되었습니다.',
      data: dto,
    };
  }
}
  • DTO를 사용하면 서버가 기대하는 요청 구조를 명확하게 만들 수 있습니다.
  • 단, TypeScript 타입만으로는 런타임 검증이 되지 않기 때문에 class-validator 같은 검증 도구가 필요합니다.

✅ 8. 페이지네이션 설계

  • 목록 API는 데이터가 많아질 수 있으므로 페이지네이션이 필요합니다.
  • 페이지네이션 없이 전체 데이터를 한 번에 내려주면 서버, DB, 브라우저 모두 부담이 커집니다.

➕ 8-1. 기본 페이지네이션 요청

GET /api/consults?page=1&limit=20
파라미터설명
page현재 페이지
limit한 페이지당 데이터 개수

➕ 8-2. 페이지네이션 응답

{
  "success": true,
  "data": {
    "items": [
      {
        "id": 1,
        "name": "홍길동",
        "phone": "01012345678"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "totalCount": 135,
      "totalPages": 7
    }
  }
}

➕ 8-3. 실무 주의점

  • limit 최대값을 제한해야 합니다.
  • 사용자가 limit=100000처럼 요청하면 서버와 DB에 큰 부하가 생길 수 있습니다.
  • 관리자 페이지에서는 검색 조건, 정렬, 날짜 필터가 함께 들어가는 경우가 많습니다.
GET /api/consults?page=1&limit=20&status=pending&startDate=2026-05-01&endDate=2026-05-28

✅ 9. REST API 설계 실무 예시

➕ 9-1. 상담 신청 API

기능메서드URL
상담 신청 등록POST/api/consults
상담 신청 목록 조회GET/api/consults
상담 신청 상세 조회GET/api/consults/:id
상담 상태 변경PATCH/api/consults/:id/status
상담 메모 수정PATCH/api/consults/:id/memo
상담 신청 삭제DELETE/api/consults/:id

➕ 9-2. 상품 API

기능메서드URL
상품 목록 조회GET/api/products
상품 상세 조회GET/api/products/:id
상품 등록POST/api/products
상품 수정PATCH/api/products/:id
상품 삭제DELETE/api/products/:id
상품 노출 여부 변경PATCH/api/products/:id/visibility

➕ 9-3. 배너 API

기능메서드URL
배너 목록 조회GET/api/banners
배너 등록POST/api/banners
배너 수정PATCH/api/banners/:id
배너 삭제DELETE/api/banners/:id
배너 순서 변경PATCH/api/banners/order
  • 완벽하게 REST 원칙을 지키는 것보다 더 중요한 것은 일관성입니다.
  • 팀이나 프로젝트 안에서 URL, 메서드, 응답 형식이 일정해야 유지보수가 쉬워집니다.

✅ 10. API 문서화

  • API는 프론트엔드와 백엔드가 함께 사용하는 약속이므로 문서화가 중요합니다.
  • 문서가 없으면 프론트엔드 개발자는 Network 탭이나 백엔드 코드를 뒤져야 하고, 백엔드는 같은 설명을 반복하게 됩니다.

➕ 10-1. 문서에 포함할 내용

  1. API 이름
  2. HTTP 메서드
  3. URL
  4. 요청 헤더
  5. 요청 파라미터
  6. 요청 바디
  7. 성공 응답 예시
  8. 실패 응답 예시
  9. 인증 필요 여부
  10. 권한 조건

➕ 10-2. 간단한 API 문서 예시

## 상담 신청 등록 API

### Method
POST

### URL
/api/consults

### Request Body
{
  "name": "홍길동",
  "phone": "01012345678",
  "model": "Galaxy S25",
  "agreeMarketing": true
}

### Response 201
{
  "success": true,
  "message": "상담 신청이 완료되었습니다.",
  "data": {
    "id": 101
  }
}

### Error 400
{
  "success": false,
  "message": "전화번호 형식이 올바르지 않습니다.",
  "errorCode": "INVALID_PHONE_NUMBER"
}

➕ 10-3. 문서화 도구

  • Swagger / OpenAPI

    • 백엔드 코드 기반으로 API 문서를 자동 생성할 수 있습니다.
    • NestJS와 함께 많이 사용합니다.
  • Postman

    • API 요청을 직접 테스트하고 팀과 공유할 수 있습니다.
  • Notion / GitHub Wiki

    • 간단한 설명, 정책, 예외 케이스를 정리하기 좋습니다.

✅ 11. REST API 설계 시 주의할 점

➕ 11-1. 프론트엔드 값을 무조건 믿지 않기

  • 브라우저에서 보내는 값은 사용자가 조작할 수 있습니다.
  • 가격, 할인율, 권한, 상태값, 사용자 ID 같은 중요한 값은 서버에서 검증해야 합니다.
위험한 예시:
프론트엔드에서 finalPrice=1000을 보내고 서버가 그대로 주문 저장

안전한 예시:
프론트엔드는 productId와 optionId만 보내고, 서버가 DB 기준으로 가격 계산

➕ 11-2. 에러 메시지에 내부 정보를 노출하지 않기

  • 에러 응답에 DB 구조, SQL 문, 서버 경로, 스택 트레이스가 그대로 노출되면 보안 위험이 생깁니다.
{
  "success": false,
  "message": "서버 처리 중 오류가 발생했습니다."
}
  • 상세 오류는 서버 로그에 남기고, 사용자에게는 필요한 수준의 메시지만 보여주는 것이 좋습니다.

➕ 11-3. API 버전 관리 고려하기

  • 운영 중인 API는 프론트엔드, 모바일 앱, 외부 연동 서비스에서 사용될 수 있습니다.
  • API 구조를 갑자기 바꾸면 기존 클라이언트가 깨질 수 있습니다.
/api/v1/products
/api/v2/products
  • 작은 프로젝트에서는 처음부터 과하게 버전을 나눌 필요는 없지만, 외부 연동이 많거나 모바일 앱이 있는 서비스에서는 버전 관리가 중요합니다.

✅ 12. 실무 체크리스트

➕ 12-1. API 설계 체크리스트

  1. URL이 리소스 중심으로 설계되었는가?
  2. HTTP 메서드가 작업 의미와 맞는가?
  3. Path Parameter와 Query Parameter가 적절히 구분되었는가?
  4. 요청 DTO가 정의되어 있는가?
  5. 필수값 검증이 적용되어 있는가?
  6. 성공 응답과 실패 응답 구조가 일정한가?
  7. 목록 API에 페이지네이션이 적용되어 있는가?
  8. 인증과 권한 체크가 필요한 API가 구분되어 있는가?
  9. 프론트엔드에서 조작 가능한 값을 서버가 다시 검증하는가?
  10. API 문서가 작성되어 있는가?

➕ 12-2. API 구현 후 테스트 체크리스트

  1. 정상 요청이 성공하는가?
  2. 필수값이 없을 때 400 오류가 나는가?
  3. 존재하지 않는 ID 요청 시 404 오류가 나는가?
  4. 권한 없는 사용자가 접근할 때 403 오류가 나는가?
  5. 응답 데이터 구조가 프론트엔드 기대값과 일치하는가?
  6. 페이지네이션 totalCount가 정확한가?
  7. 검색 조건과 정렬이 정상 동작하는가?
  8. 서버 로그에 불필요한 에러가 남지 않는가?
  9. Swagger나 Postman 문서가 최신 상태인가?
  10. 모바일/운영 환경에서도 같은 API가 정상 동작하는가?

📌 요약

  • REST API는 HTTP 기반으로 클라이언트와 서버가 데이터를 주고받기 위한 API 설계 방식입니다.
  • REST API에서는 데이터를 리소스로 보고, URL은 명사 중심으로 설계하며, 작업은 HTTP 메서드로 구분합니다.
  • Path Parameter는 특정 리소스를 식별할 때 사용하고, Query Parameter는 검색, 정렬, 필터, 페이지네이션 조건에 사용합니다.
  • 요청 데이터는 DTO로 구조화하고, 서버에서 반드시 유효성 검증을 해야 합니다.
  • 응답 데이터는 성공/실패 구조를 일관되게 설계해야 프론트엔드에서 처리하기 쉽습니다.
  • 목록 API에는 페이지네이션을 적용해야 하며, limit 최대값을 제한해 서버와 DB 부하를 막아야 합니다.
  • API 문서화는 프론트엔드와 백엔드 사이의 약속을 명확하게 만드는 작업이며, Swagger, Postman, Notion 등을 활용할 수 있습니다.
  • 실무에서는 완벽한 REST보다 일관성, 검증, 문서화, 보안이 더 중요합니다.

0개의 댓글