개발 가이드 백엔드 컨트롤러 작성 가이드
최종 수정:

컨트롤러 작성 가이드

Spring MVC Controller 작성 규칙 및 REST API 설계

컨트롤러 작성 가이드

기본 원칙

  • Controller는 요청/응답 처리만 담당한다. 비즈니스 로직을 포함하지 않는다.
  • @RequiredArgsConstructor로 생성자 주입을 사용한다.
  • @Slf4j로 로그를 선언한다.
  • Entity를 직접 반환하지 않는다. 반드시 VO(Res)로 변환 후 반환한다.

화면 컨트롤러 (Thymeleaf)

@Controller
@RequestMapping("/notes")
@RequiredArgsConstructor
@Slf4j
public class NotesController {

    private final PostService postService;

    @GetMapping
    public String list(@RequestParam(defaultValue = "0") int page,
                       @RequestParam(required = false) String category,
                       Model model) {
        model.addAttribute("posts", postService.getPostPage(page, category));
        model.addAttribute("categories", postService.getCategoryList());
        return "notes/list";
    }

    @GetMapping("/{id}")
    public String detail(@PathVariable Long id, Model model) {
        model.addAttribute("post", postService.getPost(id));
        return "notes/detail";
    }
}

REST API 컨트롤러

@RestController
@RequestMapping("/api/v1/posts")
@RequiredArgsConstructor
@Slf4j
public class PostApiController {

    private final PostService postService;

    @PostMapping
    public ResponseEntity<ApiResponse<PostDetailRes>> regPost(
            @RequestBody @Valid PostCreateReq req) {
        log.info("노트 등록 요청: title={}", req.getTitle());
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(ApiResponse.ok(postService.regPost(req)));
    }

    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<PostDetailRes>> getPost(@PathVariable Long id) {
        return ResponseEntity.ok(ApiResponse.ok(postService.getPost(id)));
    }

    @PutMapping("/{id}")
    public ResponseEntity<ApiResponse<PostDetailRes>> uptPost(
            @PathVariable Long id,
            @RequestBody @Valid PostUpdateReq req) {
        return ResponseEntity.ok(ApiResponse.ok(postService.uptPost(id, req)));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<ApiResponse<Void>> delPost(@PathVariable Long id) {
        postService.delPost(id);
        return ResponseEntity.ok(ApiResponse.ok(null));
    }
}

REST URL 규칙

GET    /api/v1/posts           # 목록 조회
GET    /api/v1/posts/{id}      # 단건 조회
POST   /api/v1/posts           # 등록
PUT    /api/v1/posts/{id}      # 전체 수정
PATCH  /api/v1/posts/{id}      # 부분 수정
DELETE /api/v1/posts/{id}      # 삭제
  • URL: 소문자 케밥케이스 (/api/v1/news-items)
  • 복수형 명사 사용 (/posts, /news-items)
  • 버전 포함 (/api/v1/)

HTTP 상태코드 기준

상황코드
조회 성공200 OK
생성 성공201 Created
잘못된 요청400 Bad Request
인증 실패401 Unauthorized
권한 없음403 Forbidden
리소스 없음404 Not Found
서버 오류500 Internal Server Error

응답 형식 (ApiResponse)

모든 REST API 응답은 com.scraping.agent.global.common.ApiResponse<T> 래퍼를 사용한다.

// 성공
return ResponseEntity.ok(ApiResponse.ok(data));

// 생성 성공
return ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.ok(data));

// 실패 (GlobalExceptionHandler에서 자동 처리)
return ResponseEntity.badRequest().body(ApiResponse.fail("에러 메시지"));

응답 JSON 형식:

{
    "success": true,
    "data": { ... },
    "error": null
}
{
    "success": false,
    "data": null,
    "error": "에러 메시지"
}

입력값 검증

// VO에 Bean Validation 어노테이션
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class PostCreateReq {

    @NotBlank(message = "제목은 필수입니다.")
    @Size(max = 200, message = "제목은 200자 이내여야 합니다.")
    private String title;

    @NotBlank(message = "내용은 필수입니다.")
    private String content;

    @NotBlank(message = "카테고리는 필수입니다.")
    private String category;
}

// 컨트롤러에서 @Valid 적용
@PostMapping
public ResponseEntity<ApiResponse<PostDetailRes>> regPost(
        @RequestBody @Valid PostCreateReq req) { ... }

검증 실패 시 GlobalExceptionHandler에서 400 응답으로 자동 처리된다.


컨트롤러 체크리스트

  • [ ] @RequiredArgsConstructor 생성자 주입 사용
  • [ ] @Slf4j 로그 선언
  • [ ] 비즈니스 로직 없음 (Service 위임)
  • [ ] Entity 직접 반환 금지 (VO 반환)
  • [ ] Request VO에 @Valid 적용
  • [ ] REST API는 ApiResponse<T> 래퍼 사용
  • [ ] URL 소문자 케밥케이스
AI 문서 검색

현재 페이지 내용을 기반으로 질문하세요.