[강의] RESTful APIs 설계와 구현

Jerry·2025년 8월 14일

REST의 이해와 실제 활용

웹의 확장성에 대한 고민

1990년대 후반, 웹 애플리케이션의 급격한 확장으로 성능 저하, 확장 한계, 유지보수 문제가 부각됨
당시 주류였던 RPC·SOAP 기반 분산 시스템은 복잡성과 높은 결합도로 인해 변화와 확장이 어려웠다.
이에 단순하고 표준화된 설계 철학을 통해 다양한 플랫폼 간 상호운용성을 확보할 필요가 제기되었다.

SOAP 아키텍처

SOAP 프로토콜 구조

Roy Fielding의 문제 의식

이러한 웹 확장성의 문제에 대해 Roy Fielding은 문제 의식을 갖고, 웹에서 확장 가능한 아키텍처 설계의 필요성을 강조하였다.

  1. 웹의 폭증과 확장성 한계
    • 1993년대 말 웹 트래픽이 급증했지만, 상태 저장 서버와 잦은 왕복 호출, 애드혹한 통신 방식이 수평 확장을 어렵게 만들었다고 보았다.
  2. 과도한 결합도와 진화 불가능성
    • RPC/CORBA/SOAP 식의 “함수 호출 중심” 통신은 서버· 클라이언트의 동시 변경을 강제하고, 인터페이스 변경이 곧 장애로 이어지는 취약한 구조라고
      지적했다.
  3. 중간 구성요소 활용 실패
    • 표준적· 자기서술적 메시지가 아니면 프록시· 캐시· 게이트웨이 같은 intermediary가 최적화, 보안, 관측성을 제공할 수 없다고 비판했다.
  4. 자원 모델의 부재
    • “동사(행위) 호출” 위주 설계는 주소 가능성(addressability)과 캐싱, 링크 탐색을 막아 웹의 본질적 강점을 살리지 못한다고 보았다.

HTTP와 웹의 성공 요인 분석

Roy Fielding은 REST를 HTTP 프로토콜과 웹 아키텍처 개선의 기준으로 활용하며, 기존 HTTP/1.x 디자인에서 다음과 같은 핵심 성공 요인을 도출하고자 했다.

성공 요인설명
확장성 지원REST의 제약을 통해 클라이언트·서버를 독립적으로 배포하고, 중간 계층(프록시·게이트웨이)과 캐시를 효과적으로 활용하여 대규모 분산 환경에서도 성능과 확장성을 유지할 수 있도록 함
표준 활용과 점진적 개선HTTP, URI, HTML 등 기존 웹 표준을 기반으로 하되, REST를 활용하여 HTTP/1.0 및 HTTP/1.1의 설계와 확장 방식을 정립하는 기준으로 삼음
자기서술적 메시지 설계메시지가 자체적으로 의미를 포함하여 프록시·캐시·게이트웨이 등 중간자

REST(Representational State Transfer) 정의

Roy Fielding은 웹이 단순한 문서 공유를 넘어 다양한 서비스와 애플리케이션의 기반이 되기 위해서는, 장기적으로 확장 가능하고 표준화된 아키텍처가 필요하다고 보았다.
그 원리를 일반화하여 REST(Representational State Transfer)라는 아키텍처 스타일을 제안했다.
REST는 인터넷의 모든 대상을 ‘자원’으로 보고, 이를 식별하는 URI와 표준 HTTP 메서드를 통해 접근하도록 설계하여, 단순함과 상호운용성을 동시에 달성하는 것을 목표로 한다.

자원(RESOURCE) - URI, URL ex) /blog/31 /animal/dog/42 /animal/cat?name=페르시안
행위(Verb) - HTTP METHOD (GET, POST, PUT, PATCH, DELETE)
표현(Representations)– JSON, XML, HTML

REST의 제약조건

REST의 제약조건은 Roy Fielding가 제시한 REST를 달성하기 위한 최소한의 제약조건 이며, 클라이언트-서버 구조, 무상태성, 캐시 처리, 계층화 시스템, 일관된 인터페이스 등과 같은 설계 규칙을 말한다.

제약조건개념 설명효과
클라이언트-서버 구조
(Client-Server)
클라이언트와 서버의 역할을 명확히 분리하여 독립적으로 개발·배포 가능하게 함확장성 향상, 역할 분리로 유지보수 용이
무상태성
(Stateless)
각 요청이 독립적으로 처리되며, 서버는 클라이언트의 상태를 저장하지 않음서버 확장 용이, 장애 복구 용이
캐시 가능성
(Cacheable)
응답이 명시적으로 캐시 가능 여부를 포함해야 함네트워크 부하 감소, 성능 향상
일관된 인터페이스
(Uniform Interface)
리소스 식별, 표현 전송, 자기서술적 메시지, HATEOAS 등으로 인터페이스를 일관되게 유지학습 곡선 완화, 상호 운용성 증가
계층화 시스템
(Layered System)
클라이언트는 중간 계층(프록시, 게이트웨이 등)을 의식하지 않고 요청 가능보안, 로드밸런싱, 확장성 강화
코드 온 디맨드
(Code on Demand)
(선택 사항– JSON 활용)
서버가 클라이언트로 실행 가능한 코드(예: JavaScript)를 전송 가능
→ JSON으로 활용되게 된 원인
클라이언트 기능 확장, 유연성 제공

Richardson 성숙도 모델 이해

Richardson 성숙도 모델은 RESTful API 성숙도를 4단계로 구분해 API가 REST 원칙을 얼마나 충실히 따르고 있는지 평가하는 모델이다. 레벨 0부터 3까지 점진적으로 자원 중심 설계, HTTP 메서드 활용, HATEOAS 적용이 강화되며, 각 단계가 높아질수록 REST 아키텍처 제약조건을 더 잘 반영한다.

모든 제약조건을 지키기 어려운 현실

REST 설계에서 이상적인 REST 제약조건을 모두 지키기 어려운 것이 현실이다.
레거시 API, 조직의 기술 스택, 유지보수 비용 등을 고려해 제약조건을 부분적으로 완화하거나 변형하는 것이 일반적이다. 이 과정에서 확장성, 단순성, 호환성 등 서로 충돌하는 요구사항 간의 균형을 찾아야 하며, 즉, API 설계 시에는 장기적인 유지보수성과 현재의 개발·운영 효율 사이에서 합리적인 트레이드오프를 결정하는 것이 핵심이다.

Open API와 REST

Open API의 개념

Open API(Open Application Programming Interface)란 누구나 접근할 수 있도록 공개된 API로, 인증 절차나 사용 정책을 준수하면 외부 개발자가 해당 API를 활용하여 서비스나 애플리케이션을 개발할 수 있도록 제공된다.
이는 조직 내부뿐 아니라 제3자 개발자와도 기능·데이터를 공유하는 표준화된 인터페이스를 의미한다.

공개 API 사례 분석 – 카카오 Open API

카카오 Open API는 로그인, 지도, 번역, 검색, 메시지 발송 등 다양한 카카오 플랫폼 기능을 외부 서비스에서 활용할 수 있도록 제공하는 API 모음이다. 카카오톡 친구/채팅방 메시지 전송, 카카오 지도 표시· 길찾기, 카카오 계정 기반 인증 등을 활용 할 수 있다.

공개 API 사례 분석 – github Open API

GitHub API는 저장소 관리, 이슈· 풀리퀘스트 처리, 사용자· 조직 정보 조회 등 GitHub 기능을 외부 애플리케이션에서 활용할 수 있도록 제공하는 RESTful API이다. 이를 통해 코드 조회· 수정, 워크플로 자동화, 협업 관리 등을 구현할 수 있다.

공개 API 사례 분석 – google 클라우드 API

Google 클라우드 API는 Google Cloud Platform의 컴퓨팅, 스토리지, 데이터 분석, AI·ML, 네트워킹 등 다양한 서비스를 프로그래밍 방식으로 제어· 활용할 수 있도록 제공하는 API이다. 이를 통해 애플리케이션에서 자원 생성·관리, 데이터 처리, AI 모델 활용 등의 기능을 구현할 수 있다.

API-First 접근 방식

API-First 접근 방식이란 애플리케이션 개발에서 API를 가장 먼저 설계하고 정의한 뒤, 이를 기반으로 클라이언트·서버·서비스 구현을 진행하는 방법론이다. 즉, API를 애플리케이션의 핵심 계약(contract)으로 간주하고, 비즈니스 요구사항과 데이터 흐름을 API 명세서로 먼저 확정한 후 개발 전반에 반영하는 것이다

RESTful API 설계 원칙

리소스 중심 API 설계

리소스 중심 API 설계는 시스템의 기능을 동작 중심이 아닌 ‘리소스(자원)’를 중심으로 식별·표현하고, 각 리소스에 대한 행위를 HTTP 메서드로 구분하는 방식이다. 이를 통해 URI는 리소스를 나타내고, GET·POST·PUT·DELETE 등의 메서드로 상태를 조회·생성·수정·삭제할 수 있다.

리소스 중심 API 설계 전략

  1. URI를 통한 리소스 표현
    • 개념 : REST에서는 모든 자원을 고유하게 식별하는 URI(Uniform Resource Identifier)를 통해 표현한다.
    • 원칙 : 명사 중심, 소문자, 복수형을 사용하고, 행위는 URI에 포함하지 않는다.
      • /users → 사용자 목록
      • /users/101 → id=101 사용자 정보
      • /blogs → 블로그 글 목록
      • /blogs/42 → id=42 블로그 글
      • /users/101/blogs → 특정 사용자의 블로그 글 목록
      • /users/101/blogs/42 → 특정 사용자의 특정 블로그 글
  2. 계층 구조 반영 방법
    • 개념 : URI 설계 시 상위-하위 관계를 경로 구조로 표현하여 자원 간의 계층적 관계를 나타낸다.
    • 원칙 : 부모-자식 관계를 /로 구분하며, 계층은 명확하게 유지한다.
      • /users/101/followers → id=101 사용자의 팔로워 목록
      • /users/101/following → id=101 사용자가 팔로우하는 사용자 목록
      • /users/101/blogs/42/comments → 특정 사용자의 블로그 글에 달린 댓글 목록
      • /users/101/blogs/42/comments/7 → 특정 댓글(id=7) 세부 정보
  3. Query Parameter vs Path Variable
    • Query Parameter : 필터링, 정렬, 페이징 등 부가적인 조건을 전달할 때 사용한다.
      • /blogs?category=it&sort=desc
    • Path Variable : 리소스의 고유 식별자를 나타내며, URI 경로의 일부로 포함된다.
      • /blogs/42 → 블로그 id=42
        → 리소스 자체를 지정할 때는 Path Variable, 리소스 집합에 대한 조건을 지정할 때는 Query Parameter를 사용한다.
  4. 리소스 식별자 설계 방법
    • 고유성: 각 자원은 유일한 식별자를 가져야 한다.
    • 안정성: 식별자는 가능한 변경되지 않도록 한다.
    • 가독성: 이해하기 쉬운 값(숫자 ID, UUID, 의미 있는 문자열 등)을 사용한다.
    • 예시
      • 숫자형 ID: /users/101, /blogs/42
      • UUID: /users/550e8400-e29b-41d4-a716-446655440000
      • 의미 있는 슬러그(Slug): /blogs/rest-api-design-principles
      • 복합 키 형태: /users/101/blogs/42 (사용자와 해당 블로그 글을 함께 식별)

HTTP 메서드의 의미와 활용

HTTP 메서드는 웹 리소스에 대한 행위(Method)를 표준화된 방식으로 정의한 것이다.
REST의 메서드는 HTTP의 GET, POST, PUT, PATCH, DELETE 등의 메서드를 통해 조회, 생성, 수정, 삭제를 수행하는 명령으로 활용된다.

메서드개념URL 예시
GET서버에서 리소스를 조회하는 안전한 요청
서버 상태를 변경하지 않음
/users
/users/101
/blogs?category=it&page=1&size=5
POST새로운 리소스를 생성하는 요청
요청 본문에 생성할 데이터 포함
/users
/blogs
/users/101/blogs
PUT/PATCH기존 리소스를 수정하는 요청
PUT: 전체 교체, PATCH: 부분 수정
PUT /users/101
PATCH /users/101
PATCH /blogs/42
DELETE기존 리소스를 삭제하는 요청/users/101
/blogs/42
/users/101/blogs/42

상태 코드의 적절한 사용

REST에서 상태 코드는 요청 처리 결과를 표준화된 숫자와 의미로 전달하는 규약으로, 클라이언트가 응답의 의미를 이해하고 적절한 후속 조치를 취할 수 있도록 돕는다. 올바른 상태 코드 사용은 API의 가독성과 신뢰성을 높인다.

범위의미예시 코드 및 설명
2xx성공(Success)200 OK: 요청 성공
201 Created: 리소스 생성 성공
3xx리다이렉션(Redirection)301 Moved Permanently: 영구적으로 URL 변경
302 Found: 임시 리다이렉션
4xx클라이언트 오류(Client Error)400 Bad Request: 잘못된 요청
401 Unauthorized: 인증 필요
404 Not Found: 리소스 없음
5xx서버 오류(Server Error)500 Internal Server Error: 서버 내부 오류
503 Service Unavailable: 서비스 불가

HTTP 헤더 활용

HTTP 헤더는 요청과 응답에 부가 정보를 담아 인증, 데이터 형식, 캐싱 등 통신 방식을 제어하는 메타데이터이다. 이를 통해 REST API의 보안, 성능, 호환성을 효율적으로 관리할 수 있다.

구분헤더 이름설명예시
요청 헤더Authorization인증 토큰 전달Authorization: Bearer <token>
요청 헤더Accept원하는 응답 데이터 타입 지정Accept: application/json
요청 헤더Content-Type요청 본문 데이터 타입 지정Content-Type: application/json
응답 헤더Content-Type서버가 반환하는 데이터 타입 명시Content-Type: application/json
응답 헤더Cache-Control캐싱 전략 설정Cache-Control: max-age=3600
응답 헤더Location새로 생성된 리소스 URI 명시Location: /users/123

API 응답 설계 개념

API 응답 설계는 클라이언트가 상태와 데이터를 일관되게 이해할 수 있도록 응답 구조를 표준화하는 과정이다. 상태 코드, 메시지, 데이터 필드를 명확히 구분해 성공·실패 상황을 예측 가능하게 전달해야 한다.

POST /users HTTP/1.1
Content-Type: application/json

{
	"username": "홍길동",
	"email": "hong@example.com",
	"role": "developer"
{
HTTP/1.1 201 Created
Content-Type: application/json
Location: /users/101
{
	"id": 101,
	"username": "홍길동",
	"email": "hong@example.com",
	"role": "developer",
	"createdAt": "2025-08-13T10:15:00Z"
}

API 응답 설계 전략

  1. 일관된 응답 구조
    • 개념 : REST API는 모든 엔드포인트에서 동일한 구조의 응답 포맷을 유지해야 클라이언트가 예측 가능하게 처리할 수 있다.
    • 예시 : 성공/실패 여부, 데이터, 에러 정보, 메타데이터 등을 동일한 계층 구조로 유지
      {
          "status": "success",
          "message": "사용자 정보 조회 성공",
          "data": {
              "id": 1,
              "name": "홍길동",
              "email": "hong@example.com"
          }
       }
  2. 성공/실패 응답 형식
    • 성공 응답
      • 2xx 상태 코드와 함께 요청 처리 결과 데이터를 반환
      • GET → 200 OK, POST → 201 Created
    • 실패 응답
      • 4xx 또는 5xx 상태 코드와 함께 오류 메시지, 에러 코드, 추가 정보를 반환
    • 성공 응답 예시
{
	"status": "success",
	"message": "블로그 글이 작성되었습니다.",
	"data": {
		"postId": 101,
		"title": "REST API 설계 가이드"
	}
}
  • 실패 응답 예시
{
	"status": "error",
	"message": "제목은 필수 입력 항목입니다.",
	"errorCode": "POST_TITLE_REQUIRED"
}
  1. 에러 처리 원칙
    • 명확성 : 오류 원인을 클라이언트가 이해할 수 있도록 구체적인 메시지와 에러 코드 제공
    • 표준 상태 코드 사용 : HTTP 상태 코드 규약에 맞춰 반환
    • 보안 고려 : 내부 시스템 정보 노출 금지
      {
          "status": "error",
          "message": "요청한 사용자를 찾을 수 없습니다.",
          "errorCode": "USER_NOT_FOUND"
      }
      상태 코드: 404 Not Found

RESTful API 구현- 기본

@RestController 개념

@RestController는 Spring MVC에서 @Controller와 @ResponseBody를 합쳐, 메서드의 반환 값을 뷰가 아닌 HTTP 응답 본문에 직접 전송하는 어노테이션이다. 주로 JSON, XML 형태로 데이터를 반환하는 RESTful API 구현에 사용된다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/users")
public class UserController {

	private final UserService userService;

	@GetMapping
	public ResponseEntity<List<User>> getAllUsers() {
		List<User> users = userService.getAllUsers();
		return ResponseEntity.ok(users); // 200 OK
	}
	
    @GetMapping("/{id}")
	public ResponseEntity<User> getUser(@PathVariable Long id) {
		User user = userService.getUserById(id);
		if (user != null) {
			return ResponseEntity.ok(user); // 200 OK
		} else {
			return ResponseEntity.notFound().build(); // 404 Not Found
		}
	}
}

@RestController 특징

구분설명예시
어노테이션 성격@Controller + @ResponseBody 결합@RestController
응답 방식반환 값을 뷰(View)가 아닌 HTTP 응답 본문에 직접 전송문자열, JSON, XML 등
데이터 변환반환 객체를 HttpMessageConverter를 통해 자동 직렬화User 객체 → JSON
주 사용 용도RESTful API, JSON/XML 기반 서비스 응답 처리API 서버, 비동기 통신
뷰 리졸버 사용 여부사용하지 않음템플릿(HTML) 렌더링 불가
메서드 예시@GetMapping, @PostMapping 등과 함께 사용@GetMapping("/users")

@RestController 어노테이션

  1. REST 컨트롤러 관련
어노테이션설명활용 예시
@RestControllerREST API 컨트롤러를 정의하며, 메서드 반환값을 자동으로 JSON/XML 형식의 HTTP 응답 본문으로 변환합니다. (=@Controller + @ResponseBody)@RestController
@RequestMapping("/users")
@ResponseBody메서드의 반환값을 HTTP 응답 본문으로 직렬화하여 직접 반환합니다. (일반적으로 @RestController 사용 시 생략)@GetMapping("/ping")
@ResponseBody
@RequestMapping클래스나 메서드에 URL 경로와 HTTP 메서드를 매핑하는 기본 어노테이션입니다. REST에서는 주로 클래스 레벨에서 경로 prefix 지정에 활용합니다.@RequestMapping(value="/users", method=RequestMethod.GET)
  1. HTTP 메서드 매핑
어노테이션설명활용 예시
@GetMappingGET 요청을 처리하며, 서버에서 리소스를 조회할 때 사용합니다.@GetMapping("/users/{id}")
@PostMappingPOST 요청을 처리하며, 새로운 리소스를 생성할 때 사용합니다.@PostMapping("/users")
@PutMappingPUT 요청을 처리하며, 기존 리소스를 전체 교체(전체 수정) 합니다.@PutMapping("/users/{id}")
@PatchMappingPATCH 요청을 처리하며, 기존 리소스의 일부만 수정 합니다.@PatchMapping("/users/{id}")
@DeleteMappingDELETE 요청을 처리하며, 지정한 리소스를 삭제합니다.@DeleteMapping("/users/{id}")
  1. 요청 데이터 매핑
어노테이션설명활용 예시
@PathVariableURL 경로의 변수를 메서드 매개변수에 매핑합니다.getUser(@PathVariable Long id)
@RequestParam쿼리 파라미터를 메서드 매개변수에 매핑합니다.getBlogs(@RequestParam String category)
@RequestBodyHTTP 요청 본문(JSON/XML)을 자바 객체로 변환해 매핑합니다.create(@RequestBody User user)
@RequestPartMultipart 요청에서 JSON 또는 파일의 일부 파트를 받아옵니다.upload(@RequestPart("meta") MetaData data, @RequestPart("file") MultipartFile file)
@ModelAttribute요청 파라미터를 모델 객체로 바인딩합니다.search(@ModelAttribute SearchForm form)
@RequestHeaderHTTP 요청 헤더 값을 매개변수에 매핑합니다.info(@RequestHeader("User-Agent") String ua)
  1. 응답/예외 처리
어노테이션설명활용 예시
@ResponseStatus메서드의 실행 결과로 반환할 HTTP 상태 코드를 지정합니다.@ResponseStatus(HttpStatus.CREATED)
@ExceptionHandler특정 예외 발생 시 처리할 메서드를 지정합니다.@ExceptionHandler(UserNotFoundException.class)
@RestControllerAdvice전역 예외 처리, 데이터 바인딩, 모델 속성 추가 등을 지원하는 전역 컨트롤러 설정입니다.@RestControllerAdvice public class GlobalHandler {}
  1. 직렬화/역직렬화 제어/포맷
어노테이션설명예시
@JsonPropertyJSON 필드 이름과 Java 필드 이름 매핑@JsonProperty("user_name")
private String username;
@JsonIgnore직렬화·역직렬화 대상에서 제외@JsonIgnore
private String password;
@JsonIgnoreProperties여러 필드를 무시할 때 사용@JsonIgnoreProperties({"password", "ssn"})
@JsonIncludenull 값이나 특정 조건의 값 제외@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonFormat날짜/시간, 숫자 포맷 지정@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;

ResponseEntity를 통한 응답 제어

ResponseEntity는 Spring에서 HTTP 응답을 세밀하게 제어할 수 있도록 제공하는 클래스이다.
이를 통해 HTTP 상태 코드, 응답 헤더, 응답 본문을 모두 설정할 수 있으며, REST API의 명확한 응답 표준화를 구현하는 데 유용하다.

@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
	Optional<User> user = userService.findById(id);
	if (user.isPresent()) {
		return ResponseEntity
			.status(HttpStatus.OK) // 상태 코드 설정
			.header("Custom-Header", "Value") // 헤더 추가
			.body(user.get()); // 본문 데이터
	} else {
		return ResponseEntity
        	.status(HttpStatus.NOT_FOUND) // 404 설정
			.body(null);
	}
}

HTTP 메시지 컨버터의 동작 방식

HTTP 메시지 컨버터(HttpMessageConverter)는 스프링 MVC에서 HTTP 요청과 응답 바디를 자바 객체와 서로 변환하는 역할을 수행한다. 컨트롤러에서 @ResponseBody나 @RestController를 통해 반환된 객체는 클라이언트 요청 형식(JSON, XML, 문자열 등)에 맞추어 직렬화된다. 또한 클라이언트가 전송한 요청 바디는 해당 컨버터를 통해 자바 객체로 역직렬화 되어 컨트롤러의 파라미터로 전달된다.
업로드중..

RestController - 요청 처리

  1. @PathVariable 활용
    • URL 경로에 포함된 변수 값을 추출하여 메서드 파라미터로 매핑하는 데 사용한다.
      예) /users/{id} 요청 시 {id} 부분을 메서드 파라미터로 바인딩할 수 있다.
      @GetMapping("/users/{id}")
      public User getUser(@PathVariable Long id) { ... }
  2. @RequestParam 활용
    • 쿼리 스트링이나 HTML 폼 데이터에서 파라미터 값을 추출하여 메서드 파라미터로 매핑하는 데 사용한다.
      예) /search?keyword=spring 요청 시 keyword 값을 메서드 파라미터로 받을 수 있다.
      @GetMapping("/blogs")
       public List<Blog> getBlogs(@RequestParam String category, @RequestParam int page) { ... }
  3. @RequestBody 활용과 검증
    • HTTP 요청 바디(JSON, XML 등)를 자바 객체로 변환하여 매핑하며, @Valid나 @Validated를 통해 유효성 검증을 할 수 있다.
      @PostMapping("/users")
      public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) { ... }
  4. @RequestPart 활용
    • multipart/form-data 요청에서 파일뿐 아니라 JSON 같은 다른 파트를 객체로 매핑할 때 사용한다. 주로 파일 업로드와 함께 JSON 데이터를 받을 때 유용하다.
      @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
      public ResponseEntity<String> uploadFile(
          @RequestPart("file") MultipartFile file,
          @RequestPart("meta") MetaData meta
      ) { ... }

RestController - 응답처리

  1. 응답 객체 설계
    REST API의 응답은 일관된 구조를 유지하는 것이 중요하다. 일반적으로 error 코드와 함께 리소스를 전달해도 무관하지만, 필요시 status, message, data 등의 필드를 명확히 구분하여 클라이언트가 상태를 쉽게 파악하고 데이터 처리를 예측 가능하게 할 수 도 있다. 성공 시에는 status와 함께 결과 데이터를, 실패 시에는 status와 errorCode, errorMessage를 포함한다.
    • 일반 응답
      {
          "id": 42,
          "title": "REST 설계 가이드",
          "author": "홍길동",
          "category": "IT 개발"
      }
  • 커스텀 응답
    {
        "status": "success",
        "message": "블로그 글 조회 성공"
        "data": {
            "id": 42,
            "title": "REST API 설계 원칙",
            "author": "홍길동"
        }
    }
    @Data
    @AllArgsConstructor
    public class ApiResponse<T> {
        private String status;
        private String message;
        private T data;
    }
  1. JSON 직렬화 제어
    응답 객체를 JSON으로 변환할 때, Jackson 어노테이션을 사용해 직렬화 동작을 제어할 수 있다.
    @JsonIgnore는 특정 필드를 숨기고, @JsonProperty는 속성명을 변경하며, @JsonInclude는 null 값 필드를 제외한다.
@Data
@AllArgsConstructor
public class Blog {
    @JsonIgnore
    private String internalCode; // JSON 응답에서 제외
  
    @JsonProperty("blog_title")
    private String title; // 속성명을 blog_title로 변경

	@JsonInclude(JsonInclude.Include.NON_NULL)
	private String category; // null이면 응답에서 제외

	@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
	private LocalDateTime createdAt; // 날짜 포맷 지정
}
  1. 다양한 응답 형식 처리
    REST API는 JSON을 기본으로 하지만, XML, CSV 등 다양한 형식의 응답을 제공할 수 있다.
    Spring의 HttpMessageConverter와 콘텐츠 협상(Content Negotiation)을 활용하면 Accept 헤더나 URL 확장자를 기반으로 자동 변환이 가능하다.
@RestController
@RequestMapping("/blogs")
public class BlogController {

	@GetMapping(value = "/{id}.json", produces = MediaType.APPLICATION_JSON_VALUE)
	public Blog getBlogJson(@PathVariable Long id) {
		return new Blog(id, "REST API 설계 원칙", "홍길동");
	}

	@GetMapping(value = "/{id}.xml", produces = MediaType.APPLICATION_XML_VALUE)
	public Blog getBlogXml(@PathVariable Long id) {
		return new Blog(id, "REST API 설계 원칙", "홍길동");
    }
}

RestController - 예외처리

  1. @ExceptionHandler 활용
    • 특정 컨트롤러 내부에서 발생하는 예외를 처리하는 메서드에 붙인다.
    • 메서드 파라미터로 예외 객체를 받아 맞춤형 응답을 반환할 수 있다.
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }
  2. @RestControllerAdvice 활용
    • 전역(Global) 예외 처리기 역할을 하며, 모든 컨트롤러에서 발생한 예외를 공통 처리한다.
    • @ControllerAdvice + @ResponseBody의 결합 형태이므로 JSON 응답을 기본으로 한다.
    @RestControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(Exception.class)
        public ResponseEntity<String> handleAll(Exception ex) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("서버 오류 발생");
        }
    }
  3. 예외별 응답 처리
    • 예외 타입별로 다른 HTTP 상태 코드와 응답 구조를 정의한다.
    • 공통 응답 객체를 만들어 상태 코드, 메시지, 에러 코드 등을 포함시킬 수 있다.
@RestControllerAdvice
public class GlobalExceptionHandler {
    // 리소스를 찾을 수 없는 경우
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ApiErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
        ApiErrorResponse errorResponse = new ApiErrorResponse(
                HttpStatus.NOT_FOUND.value(),
                ex.getMessage(),
                "RESOURCE_NOT_FOUND"
        );
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse);
    }
    
    // 그 외 서버 오류 처리
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiErrorResponse> handleGeneralException(Exception ex) {
        ApiErrorResponse errorResponse = new ApiErrorResponse(
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                "서버 내부 오류가 발생했습니다.",
                "INTERNAL_SERVER_ERROR"
        );
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
    }
}

@Getter
@AllArgsConstructor
public class ApiErrorResponse {
    private int status;
    private String message;
    private String errorCode;
}
profile
Backend engineer

0개의 댓글