앞선 포스팅에서는 기획서의 비즈니스 요구사항을 차가운 서버의 논리로 번역하는 'ERD(데이터베이스) 설계'에 대해 다루었습니다. 데이터의 뼈대를 튼튼하게 세웠다면, 이제 그 데이터를 밖으로 꺼내어 사용자에게 전달할 통로를 만들어야 합니다.
백엔드 서버와 프론트엔드(Web/iOS/Android) 클라이언트가 데이터를 주고받는 소통의 다리, 바로 API(Application Programming Interface)입니다.
초보 백엔드 개발자들은 기능이 동작하는 것에만 급급하여 자기 마음대로 API 주소를 짓곤 합니다. 하지만 API는 프론트엔드 개발자와 맺는 `엄격한 계약(Contract)'입니다. 오늘은 실무에서 프론트엔드 개발자에게 사랑받는(그리고 야근을 줄여주는) RESTful API 설계 원칙과, 서비스가 확장될 때 앱이 터지는 대참사를 막기 위한 API 버저닝(Versioning) 전략을 실무자의 시선으로 해부해 보겠습니다.
1. 동사(Verb)를 버리고 명사(Noun)를 취하라: RESTful의 핵심
과거에는 API 엔드포인트(주소)를 지을 때 개발자의 의식의 흐름대로 짓는 경우가 많았습니다. 예를 들어 유저 정보를 가져오려면 /getUsers, 유저를 삭제하려면 /deleteUser 같은 식이었죠.
하지만 REST(Representational State Transfer) 아키텍처가 표준으로 자리 잡으면서 규칙이 명확해졌습니다. "URL(URI)은 자원(Resource, 명사)만 표현하고, 행위(Action)는 HTTP 메서드(GET, POST, PUT, DELETE)에 맡긴다"는 것입니다.
나쁜 예시 (초보자의 냄새가 나는 API)
* GET /getAllContents (모든 콘텐츠 가져오기)
* POST /updateContentData (콘텐츠 수정하기)
좋은 예시 (RESTful한 실무 API)
* GET /contents (모든 콘텐츠 가져오기)
* PUT /contents/{content_id} (특정 콘텐츠 수정하기)
* DELETE /contents/{content_id} (특정 콘텐츠 삭제하기)
이렇게 설계하면 프론트엔드 개발자는 API 명세서(Swagger 등)를 굳이 전부 읽어보지 않아도, URL 구조만으로 "아, 이 주소에 DELETE 요청을 보내면 해당 콘텐츠가 지워지겠구나"라고 직관적으로 유추할 수 있습니다. 소통 비용이 획기적으로 줄어드는 것이죠.
2. 스타트업 대참사를 막는 생명줄: API 버저닝(Versioning)
사이드 프로젝트나 초기 기획 단계에서는 API 주소를 /api/users 처럼 단순하게 만듭니다. 하지만 이 서비스가 런칭을 하고, 구글 플레이스토어나 앱스토어에 앱이 배포되는 순간 상황은 180도 달라집니다.
끔찍한 실무 시나리오
> 비즈니스 로직이 바뀌어서 백엔드 개발자가 유저 프로필 조회 API(/api/users)의 응답 데이터 구조를 완전히 갈아엎었습니다. 그리고 서버를 배포했습니다.
> 결과는? 업데이트를 하지 않은 구버전 앱을 쓰는 모든 사용자의 앱이 그 즉시 하얀 화면을 띄우며 크래시(Crash) 나고 뻗어버립니다.
>
모바일 앱은 웹사이트처럼 새로고침한다고 최신 코드가 반영되지 않습니다. 유저가 스토어에서 업데이트 버튼을 누르기 전까지는 과거의 API 구조를 기대하며 서버에 요청을 보냅니다. 따라서 백엔드는 '과거의 앱'과 '최신의 앱' 모두를 지원해야 할 의무가 있습니다.
이때 필요한 것이 바로 URI 버저닝(Versioning)입니다.
-- 실무에서 반드시 지켜야 할 API 주소 체계
[v1] GET /api/v1/users (구버전 앱 유저들을 위해 남겨두는 구형 로직)
[v2] GET /api/v2/users (새로 업데이트된 앱 유저들이 호출할 신형 로직)
처음 프로젝트를 세팅할 때부터 모든 API 주소 앞에 /v1/을 붙이는 습관을 들이세요. 훗날 비즈니스가 확장되어 데이터베이스 구조가 통째로 바뀌더라도, 구버전 유저들을 안전하게 보호하며 점진적으로 마이그레이션할 수 있는 튼튼한 방어막이 되어줍니다.
3. 프론트엔드의 분노를 유발하는 에러: 200 OK의 함정
프론트엔드 개발자와 백엔드 개발자가 가장 크게 충돌하는 지점이 바로 '에러 처리' 방식입니다.
초보 백엔드 개발자 중에는 서버 로직 안에서 예외(Exception)를 캐치했다는 이유로, 로그인이 실패하거나 데이터를 찾을 수 없을 때조차 HTTP 상태 코드를 **200 OK**로 내려보내는 경우가 많습니다.
// ❌ 프론트엔드가 경악하는 최악의 응답 (HTTP Status: 200 OK)
{
"status": "fail",
"error_code": 404,
"message": "해당 사용자를 찾을 수 없습니다."
}
분명히 실패한 요청인데 네트워크 상태 코드는 '200 성공'으로 오기 때문에, 프론트엔드의 axios나 fetch 라이브러리는 이를 에러(Catch)로 인식하지 못하고 성공(Then) 로직으로 넘겨버립니다. 프론트엔드 단에서 일일이 "status" == "fail"인지 확인하는 스파게티 코드를 짜야 하죠.
실무 대처법: 일관된 공통 응답 폼(Response Wrapper)과 상태 코드 활용
실무에서는 무조건 HTTP 표준 상태 코드(400 Bad Request, 401 Unauthorized, 404 Not Found 등)를 정확하게 반환해야 합니다. 그와 동시에, 프론트엔드가 데이터를 일관되게 파싱할 수 있도록 모든 응답을 하나의 '공통 JSON 포맷'으로 감싸서(Wrapping) 보내는 것이 백엔드 아키텍처의 기본입니다.
# 파이썬(FastAPI)에서 공통 응답 포맷을 구조화한 예시
class ResponseDTO(BaseModel):
success: bool
data: Optional[Any] = None
error_message: Optional[str] = None
# 정상 처리 시 (HTTP 200)
# { "success": true, "data": {"user_id": 1, "name": "Jinho"}, "error_message": null }
# 클라이언트 에러 시 (HTTP 404)
# { "success": false, "data": null, "error_message": "사용자를 찾을 수 없습니다." }
이렇게 백엔드에서 규격을 하나로 꽉 잡아주면, 프론트엔드에서는 네트워크 에러 처리 로직을 한 곳(Interceptor)으로 모아서 매우 우아하게 코드를 짤 수 있습니다. "API 잘 짜는 백엔드 개발자"라는 찬사는 덤으로 따라오죠.
4. 마무리 (백엔드는 신뢰를 파는 직업이다)
API는 단순한 데이터 전달 통로가 아닙니다. 프론트엔드, 모바일 앱, 그리고 훗날 우리 서비스의 데이터를 연동할 외부 파트너사들과 맺는 단단한 신뢰의 계약'입니다.
RESTful 원칙을 지켜 예측 가능하게 만들고, 버저닝을 통해 과거의 고객을 버리지 않으며, 명확한 에러 코드로 동료의 퇴근 시간을 앞당겨주는 것. 이것이 진짜 시니어 백엔드 엔지니어의 시야입니다.
데이터베이스(ERD)를 짓고, 프론트엔드와의 통신 규약(API)까지 완벽하게 세웠습니다. 그렇다면 이제 우리가 만든 이 멋진 백엔드 서버를 내 컴퓨터(Localhost)를 넘어 전 세계 사람들이 접속할 수 있는 '진짜 서버'에 올릴 차례겠죠?
다음 포스팅에서는 현대 백엔드 배포의 표준이자 무기인 'Docker(도커) 기반의 컨테이너화와 클라우드 배포 기초 아키텍처'에 대해 파헤쳐 보겠습니다.
'인공지능(AI)' 카테고리의 다른 글
| 배포는 끝이 아니라 시작이다: 24시간 꺼지지 않는 서버를 위한 로깅과 모니터링 전략 (0) | 2026.04.01 |
|---|---|
| Docker(도커)를 활용한 컨테이너화와 클라우드 배포 기초 (0) | 2026.03.31 |
| 기획서의 텍스트를 백엔드 코드로 번역하는 첫 단추: 비즈니스 요구사항 기반의 ERD(데이터베이스) 설계 실무 (0) | 2026.03.22 |
| 투자자를 설득하는 기획의 정수: 사업계획서 PEST 환경 분석 실무 (0) | 2026.03.19 |
| 외주 개발 실패를 막는 기획자의 무기: 완벽한 IT 서비스 제안서(RFP) 작성 실무 가이드 (0) | 2026.03.18 |