[데브코스] Spring Boot REST API 실습 (13강) - 조회가 아닌 경우, resultCode와 msg가 있는 응답이 좋은 이유

zuno·2025년 12월 21일

이번 글은 데브코스 Spring Boot REST API 실습 13강 내용을 정리한 글이다.
13강에서는 새로운 API 기능을 추가하기보다는,
REST API 응답 설계 관점에서 중요한 원칙을 다룬다.

특히 다음 질문에 대한 답을 다룬다.

  • 왜 조회 API와 삭제 API의 응답 형태를 통일해야 할까?
  • Map을 반환했는데 왜 JSON으로 잘 나올까?
  • resultCode, msg는 왜 필요한가?
  • 200-1 같은 resultCode는 어떤 의미일까?

1️⃣ 13강의 핵심 주제

13강의 핵심 메시지는 한 문장으로 정리할 수 있다.

조회가 아닌 API의 응답에는
resultCode와 msg가 포함된 JSON 형태가 좋다.

이유는 프론트엔드와의 협업 때문이다.


2️⃣ 기존 상황 정리

현재 API 응답 형태는 다음과 같았다.

조회 API

{
  "id": 1,
  "content": "댓글 내용",
  "createdDate": "2025-12-21T01:47:02"
}

삭제 API (기존)

1번 댓글이 삭제되었습니다.

조회는 JSON 객체인데,
삭제는 문자열(String) 응답이다.

👉 강사님은 이 부분을 보고 이렇게 설명하셨다.

“통일성이 있으면 좋다.”


3️⃣ 왜 응답 통일성이 중요할까?

REST API는 프론트엔드와의 계약이다.

프론트엔드 입장에서 중요한 것은:

  • 항상 같은 방식으로 성공/실패를 판단할 수 있는가?
  • 응답 구조가 예측 가능한가?

예를 들어 프론트에서 이런 코드를 작성한다고 가정해보자.

if (res.resultCode.startsWith("200")) {
    // 성공 처리
}

조회는 객체,
삭제는 문자열이면
이런 공통 처리가 불가능해진다.

👉 그래서 조회/삭제/수정/생성 모두 JSON 객체로 통일하는 것이 좋다.


4️⃣ Map을 반환했는데 왜 JSON으로 나올까?

삭제 API는 다음과 같이 수정되었다.

@GetMapping("/{id}/delete")
@Transactional
public Map<String, Object> delete(
        @PathVariable int postId,
        @PathVariable int id
) {
    Post post = postService.findById(postId).get();
    PostComment postComment = post.findCommentById(id).get();

    postService.deleteComment(post, postComment);

    Map<String, Object> rsData = new LinkedHashMap<>();
    rsData.put("resultCode", "200-1");
    rsData.put("msg", "%d번 댓글이 삭제되었습니다.".formatted(postComment.getId()));

    return rsData;
}

여기서 중요한 점은:

Spring + Jackson은
Map, List, DTO, record 모두 JSON으로 변환할 수 있다.

즉,

  • Map → JSON 객체
  • List → JSON 배열
  • DTO/record → JSON 객체

그래서 Map을 반환해도 자연스럽게 JSON 응답이 내려간다.


5️⃣ 왜 LinkedHashMap을 사용했을까?

Map<String, Object> rsData = new LinkedHashMap<>();

LinkedHashMap넣은 순서를 유지한다.

그래서 응답 JSON이 보통 다음 순서로 나온다.

{
  "resultCode": "200-1",
  "msg": "1번 댓글이 삭제되었습니다."
}

응답을 사람이 읽기에도 깔끔해진다.


6️⃣ resultCode 설계 의도

13강에서 사용한 resultCode는 단순한 문자열이 아니다.

resultCode 형식

HTTP상태코드-세부구분코드

예시

200-1

의미는 다음과 같다.

  • 200 : HTTP 상태 코드 (성공)
  • - : 구분자
  • 1 : 세부 상황 코드 (댓글 삭제 성공)

즉,

200-1 = “HTTP 200 성공 + 댓글 삭제 성공”

이런 방식의 장점은 다음과 같다.

  • HTTP 상태 코드와 의미를 함께 표현
  • 성공/실패를 문자열 하나로 명확히 구분 가능
  • 프론트엔드에서 분기 처리하기 쉬움

7️⃣ 왜 조회 API에는 resultCode를 안 쓸까?

조회 API는 이미 응답 자체가 데이터다.

{
  "id": 1,
  "content": "댓글 내용"
}

조회는:

  • 데이터가 있으면 성공
  • 없으면 예외 처리

이 구조만으로도 충분하다.

반면,

  • 삭제
  • 생성
  • 수정

같은 행위(action) API
결과 메시지와 성공 여부를 함께 전달하는 것이 좋다.


8️⃣ Map 응답의 한계와 다음 단계

Map으로 응답을 통일하는 방식은:

  • 학습 단계에서는 매우 좋다
  • 빠르게 구조를 맞출 수 있다

하지만 단점도 있다.

  • 키 이름 오타를 컴파일 타임에 잡을 수 없음
  • 타입 안정성이 떨어짐
  • 응답 스펙이 명확하지 않음

그래서 보통 다음 단계로 발전한다.

Map<String, Object>
↓
RsData<T> 같은 공통 응답 DTO

이건 이후 강의에서 자연스럽게 다뤄질 주제다.


🔚 13강 정리

  • 조회 API와 삭제 API의 응답 형태는 통일하는 것이 좋다
  • Map을 반환해도 Jackson이 JSON으로 변환해준다
  • 조회가 아닌 API는 resultCode, msg 응답이 유용하다
  • resultCode = HTTP상태코드-세부코드 형태는 협업에 유리하다
  • 이는 프론트엔드와의 계약을 명확히 하는 설계다

💡 한 줄 요약

13강은 API 기능보다
“응답을 어떻게 설계해야 협업하기 좋은가”를 알려주는 강의였다.

0개의 댓글