[데브코스] Spring Boot REST API 실습 (18강) - RsData에 제네릭 적용해서 Object 제거 (범용 응답 완성)

zuno·2025년 12월 24일

이번 강의(18강)에서는 드디어 RsData제네릭(Generic) 으로 바꾸면서
이전 강의에서 임시로 사용했던 Object 타입을 제거한다.

이걸로 인해:

  • 글 응답 / 댓글 응답 모두 RsData 하나로 통일 가능
  • 타입 안정성 확보
  • 도메인별 RsData 클래스(ForPostRsData 등) 필요 없음

즉, 응답 구조가 “진짜 실무형”으로 완성되는 단계였다.


1️⃣ 이전 강의(17강)의 문제점: RsData의 data가 Object였다

17강에서는 RsData가 이렇게 생겼다.

public record RsData(String resultCode, String msg, Object data) {
}

이 구조도 JSON 응답은 잘 되지만, 문제는 명확했다.

  • 컴파일 타임에 타입 체크 불가
  • data에 무엇이 들어가는지 코드만 보고 알기 어려움
  • 프론트 협업 관점에서도 응답 스펙이 흐려짐

그래서 강의에서도 “Object는 지양하는 게 좋다”는 방향성을 잡았다.


2️⃣ 18강 해결책: RsData에 제네릭 적용

18강에서 RsData는 이렇게 변경된다.

package com.back.global.rsData;

public record RsData<T>(String resultCode, String msg, T data) {
}

이제 data는 Object가 아니라 T(타입 파라미터) 로 결정된다.

✅ 장점

  • RsData<PostDto> → data는 PostDto만 가능
  • RsData<PostCommentDto> → data는 PostCommentDto만 가능
  • 타입이 명확하니 응답 설계가 더 선명해짐
  • IDE 자동완성 / 컴파일러 체크까지 도움됨

3️⃣ 글 삭제 API에 제네릭 RsData 적용

글 삭제 API는 다음과 같이 변경된다.

@GetMapping("/{id}/delete")
@Transactional
public RsData<PostDto> delete(@PathVariable int id) {
    Post post = postService.findById(id).get();

    postService.delete(post);

    return new RsData<>(
            "200-1",
            "%d번 글이 삭제되었습니다.".formatted(id),
            new PostDto(post)
    );
}

여기서 중요한 포인트:

  • 반환 타입이 RsData<PostDto> 로 명확해짐
  • new RsData<>(...) 처럼 생성자에서는 <>만 써도 됨
    → 자바의 타입 추론(Type Inference) 덕분에 PostDto가 자동으로 결정된다.

4️⃣ “놀랍게도 RsData라고 안 쓰고 RsData 라고 써도 되긴 된다?”

강의에서 언급된 포인트가 이거였다.

컨트롤러 메서드 반환 타입에 RsData<PostDto>가 아니라 RsData라고만 써도 동작은 한다.

이게 가능한 이유는 단순하다.

  • 제네릭은 컴파일 타임 타입 안정성을 위한 장치
  • 런타임에는 타입 소거(Type Erasure) 로 인해 RsData<T>의 T 정보가 사라짐
  • 그래서 RsData(raw type)로 써도 자바는 컴파일이 되긴 함

하지만 강의에서 강조한 것처럼:

RsData(raw type)는 사실상 RsData<Object>처럼 되어버린다.

즉, 동작은 해도…

  • 타입 안정성이 무너지고
  • Object를 쓰는 것과 비슷해지고
  • 결국 17강으로 되돌아가는 셈

👉 그래서 지양하는 게 좋다.


5️⃣ 댓글 삭제 API도 제네릭 적용

댓글 삭제도 마찬가지로 제네릭을 적용한다.

@GetMapping("/{id}/delete")
@Transactional
public RsData<PostCommentDto> delete(
        @PathVariable int postId,
        @PathVariable int id
) {
    Post post = postService.findById(postId).get();

    PostComment postComment = post.findCommentById(id).get();

    postService.deleteComment(post, postComment);

    return new RsData<>(
            "200-1",
            "%d번 댓글이 삭제되었습니다.".formatted(id),
            new PostCommentDto(postComment)
    );
}

이제 댓글 삭제 응답도:

  • resultCode, msg는 공통
  • data는 항상 PostCommentDto로 고정

응답 스펙이 코드만 봐도 완전히 명확해졌다.


6️⃣ 18강을 통해 응답 설계가 “완성”된 느낌

이번 강의 흐름을 한 줄로 정리하면 이렇다.

  • 15강: RsData에 data 추가 필요성 체감
  • 16강: 도메인별 RsData 폭증 문제 발생
  • 17강: Object로 통합(되긴 하지만 찝찝함)
  • ✅ 18강: 제네릭 적용으로 완전 해결

즉, “왜 제네릭이 필요한지”를
강의를 따라가며 자연스럽게 납득시키는 흐름이었다.


🔚 정리

  • Object data는 편하지만 타입 안정성이 없다
  • RsData<T> 제네릭을 적용하면:
    • 응답 구조는 하나로 통일되고
    • 도메인별 응답 클래스를 만들 필요가 없고
    • 타입 안정성까지 확보된다
  • RsData만 쓰는(raw type) 방식은 사실상 RsData<Object>와 같아서 지양해야 한다

💡 한 줄 요약

18강은 “공통 응답 RsData”를
진짜 범용 + 타입 안전한 형태로 완성한 강의였다.

0개의 댓글