개발천재

[Spring Boot] @RestController, 효과적인 API 개발을 위한 핵심 가이드 본문

개발 준비/Spring Boot

[Spring Boot] @RestController, 효과적인 API 개발을 위한 핵심 가이드

세리블리 2025. 3. 4. 22:11
반응형

@RestController 이해하기

@RestController는 Spring에서 RESTful 웹 서비스를 쉽게 만들 수 있도록 도와주는 어노테이션이다.

 

내부적으로 @Controller와 @ResponseBody를 포함하고 있어, 별도로 @ResponseBody를 선언하지 않아도 자동으로 JSON 형식의 데이터를 반환한다. 주로 API를 개발할 때 사용되며, 클라이언트와 서버 간 데이터를 주고받는 엔드포인트를 만들 때 유용하다. 이를 활용하면 HTTP 요청을 처리하고, 적절한 응답을 ResponseEntity와 함께 쉽게 반환할 수 있어 RESTful API 개발을 보다 직관적이고 효율적으로 구현할 수 있다.

 

 


 

 

@RestController의 장점

@RestController의 큰 장점은 JSON 데이터를 자동으로 반환해주는 것이다.

 

기존의 @Controller에서는 데이터를 응답할 때 @ResponseBody를 붙여야 했지만, @RestController를 사용하면 별도의 설정 없이도 JSON 응답을 제공한다. 즉, API 서버를 만들 때 코드가 간결해지고, 프론트엔드(React, Vue 등)나 모바일 앱과 쉽게 데이터를 주고받을 수 있다.

 

✔ HTML을 반환하지 않고 JSON 데이터를 반환하는 API를 쉽게 만들 수 있음
✔ @ResponseBody를 따로 붙일 필요 없음
✔ RESTful API 서버 개발에 적합

 

 


 

 

@RestController과 @Controller의 차이점

@RestController는 HTML을 반환하지 않고 데이터를 반환하는 API를 만들 때 사용한다.

어노테이션 설명 반환값
@Controller 일반적인 웹 페이지 반환 (HTML) View (.html, .jsp)
@RestController REST API 응답을 위한 컨트롤러 JSON or XML

 

 

@Controller 사용 예제 (HTML 반환)
articleView.html을 반환하여 웹 페이지를 보여준다.

@Controller
@RequestMapping("/articles")
public class ArticleController {
    
    @GetMapping("/{id}")
    public String getArticle(Model model, @PathVariable Long id) {
        model.addAttribute("article", new Article(id, "제목", "내용"));
        return "articleView"; // `articleView.html`을 반환
    }
}

 

 

@RestController 사용 예제 (JSON 반환)

웹 브라우저 또는 Postman, API 테스트 툴에서 JSON 데이터가 출력된다.

@RestController
@RequestMapping("/api/articles")
public class ArticleRestController {
    
    @GetMapping("/{id}")
    public Article getArticle(@PathVariable Long id) {
        return new Article(id, "제목", "내용"); // JSON 형태로 응답
    }
}
{
  "id": 1,
  "title": "제목",
  "content": "내용"
}

.

 


 

 

@ResponseBody 없이 JSON을 반환하는 이유

@RestController는 내부적으로 모든 메서드에 @ResponseBody가 자동 적용된다. @ResponseBody는 메서드의 반환값을 뷰(HTML) 대신 JSON이나 XML 같은 데이터로 변환하는 역할을 한다.


@Controller를 사용하면 기본적으로 뷰(HTML 페이지)를 반환하는 구조이기 때문에, JSON이나 문자열 데이터를 응답하려면 @ResponseBody를 명시적으로 선언해야 한다. 하지만 @RestController는 @Controller와 @ResponseBody를 합친 개념이므로, 모든 메서드에 자동으로 @ResponseBody가 적용된다. 즉, 별도의 어노테이션 없이도 객체를 반환하면 Spring이 이를 JSON 형식으로 변환하여 응답한다. 덕분에 RESTful API 개발이 간결해지고, 프론트엔드나 모바일 클라이언트와 데이터를 주고받는 작업이 훨씬 편리해진다.

@Controller
@RequestMapping("/api/articles")
public class ArticleController {
    
    @GetMapping("/{id}")
    @ResponseBody  // JSON으로 반환
    public Article getArticle(@PathVariable Long id) {
        return new Article(id, "제목", "내용");
    }
}

 

 


 

 

@RestController와 JSON 데이터 전송 (@PostMapping)

@RequestBody를 사용하면, 클라이언트가 보낸 JSON 데이터를 Comment 객체로 변환해준다.

@RestController
@RequestMapping("/api/comments")
public class CommentRestController {

    @PostMapping
    public Comment createComment(@RequestBody Comment comment) {
        System.out.println(comment);
        return comment; // 저장된 데이터 반환
    }
}

 

Postman이나 프론트엔드에서 아래 JSON을 전송하면 된다.

{
  "id": 1,
  "nickname": "홍길동",
  "body": "안녕하세요!"
}

 

 


 

 

@RestController예시

@RestController
public class CommentApiController {
    private final CommentService commentService;
    private final ArticleService articleService;

    public CommentApiController(CommentService commentService, ArticleService articleService) {
        this.commentService = commentService;
        this.articleService = articleService;
    }

    // 0. 예외 테스트
    @GetMapping("api/exception")
    public String exceptionHandler(){
        throw new BadRequestException("예외 처리 테스트");
    }

    // 1. 댓글 조회
    @GetMapping("/api/comments/{commentId}")
    public ResponseEntity<?> commentSearch(
            @PathVariable("commentId") Long commentId
    ) {
        CommentDTO findComment = getDto(commentId, "댓글 조회에 실패했습니다");
        return ResponseEntity.status(HttpStatus.OK).body(findComment);
    }

    // 2. 댓글 생성
    @PostMapping("/api/articles/{articleId}/comments")
    public ResponseEntity<?> commentCreate(
            @PathVariable("articleId") Long articleId,
            @RequestBody CommentDTO dto
    ) {
        // 던져진 articleId가 존재하는지 확인
        ArticleDTO existsArticle = articleService.getOneArticle(articleId);
        // 없는 게시글이면 Exception 처리
        if (ObjectUtils.isEmpty(existsArticle)) {
            throw new BadRequestException("게시글 ID가 존재하지 않습니다.");
        } else {
            // 존재하면 댓글 추가하기
            commentService.insertComment(articleId, dto);
            return ResponseEntity.status(HttpStatus.OK)
                    .body(ApiResponseMessage.builder().message("댓글 생성 성공").build());
        }
    }

    // 3. 댓글 수정
    @PatchMapping("/api/comments/{commentId}")
    public ResponseEntity<?> commentUpdate(
            @PathVariable("commentId") Long commentId,
            @RequestBody CommentDTO dto
    ) {
        // URL의 commentId와 RequestBody의 commentId 가 동일한지 확인
        if (!dto.getCommentId().equals(commentId)) {
            throw new BadRequestException("댓글 수정 요청 오류");
        }
        // 수정 요청 commentId가 존재하는지 확인
        CommentDTO result = getDto(commentId, "댓글 수정 실패");
        // 존재하면 수정 요청
        commentService.updateComment(dto);
        return ResponseEntity.status(HttpStatus.OK)
                .body(ApiResponseMessage.builder().message("댓글 수정 성공").build());
    }

    // 4. 댓글 삭제
    @DeleteMapping("/api/comments/{commentId}")
    public ResponseEntity<?> commentDelete(
            @PathVariable("commentId") Long commentId
    ) {
        // 삭제할 댓글 아이디 확인
        CommentDTO result = getDto(commentId, "댓글삭제실패");

        // 삭제 요청하기
        commentService.deleteComment(result.getCommentId());

        return ResponseEntity.status(HttpStatus.OK)
                .body(ApiResponseMessage.builder().message("댓글 삭제 성공").build());
    }

    private CommentDTO getDto(Long commentId, String message) {
        Map<String, Object> findComment = commentService.findByCommentId(commentId);
        if (ObjectUtils.isEmpty(findComment.get("dto"))) {
            throw new BadRequestException(message);
        }
        return (CommentDTO) findComment.get("dto");
    }
}
반응형