# API Controller 기준 ## 1. 목적 이 문서는 Spring MVC 기반 API controller의 책임, 위치, 작성 방식, 금지사항을 정의한다. 이 문서의 목표는 다음과 같다. - controller를 HTTP boundary 역할에 집중시킨다. - request parsing / validation / authentication context extraction / response shaping의 위치를 명확히 한다. - business rule, transaction, persistence access, external integration이 controller로 새어 들어오지 않게 한다. - API controller가 프로젝트 전체에서 일관된 구조를 가지게 한다. ## 2. 근거 수준 - Official: Spring Framework / Spring Boot 공식 문서에서 직접 확인되는 내용 - Official + Practice: 공식 문서의 확장 지점 위에 일반적인 실무 API 설계 원칙을 결합한 내용 - Project Recommendation: 이 프로젝트 구조와 운영 방식에 맞춘 규칙 ## 3. 기본 원칙 ### 3.1 API endpoint는 기본적으로 @RestController를 사용한다 Spring 공식 문서 기준으로 @RestController는 @Controller와 @ResponseBody를 결합한 annotation이며, @RequestMapping 메서드가 기본적으로 response body semantics를 가진다. JSON/HTTP body를 반환하는 API controller의 기본 선택지는 @RestController다. HTML view rendering이 목적일 때만 @Controller를 사용한다. 프로젝트 규칙: - REST API endpoint의 기본값은 @RestController - 서버사이드 템플릿 렌더링 등 view name 반환이 목적일 때만 @Controller - 같은 controller 안에 API 응답과 view 렌더링을 섞지 않는다 ### 3.2 controller는 HTTP boundary translator다 Spring MVC는 controller 메서드에서 request mapping, request input, exception handling, return value rendering을 담당할 수 있게 해 준다. 그러나 이 프로젝트에서 controller의 핵심 책임은 “HTTP 요청을 애플리케이션 입력으로 번역하고, 애플리케이션 결과를 HTTP 응답으로 번역하는 것”이다. 프로젝트 규칙: - controller는 request를 해석한다 - 필요한 인증 주체/식별자/입력을 추출한다 - application service / use case를 호출한다 - 응답 DTO 또는 ApiResult로 응답을 만든다 그 외의 핵심 business decision은 controller의 기본 책임이 아니다. ### 3.3 controller는 얇게 유지한다 이 항목은 Official + Practice + Project Recommendation 이다. 프로젝트 규칙: - controller는 가능한 한 짧고 예측 가능해야 한다 - HTTP concern 외의 분기와 규칙은 application/domain으로 이동한다 - controller 하나가 여러 repository, external client, transaction concern을 직접 조립하지 않는다 - “request 파싱 → use case 호출 → response 반환” 흐름이 한눈에 보여야 한다 ## 4. 매핑 표준 ### 4.1 class-level base path + method-level endpoint를 사용한다 Spring 공식 문서 기준으로 @RequestMapping은 class level에서 shared mapping을, method level에서 구체 endpoint mapping을 표현할 수 있다. 또한 대부분의 controller method는 모든 HTTP method를 받는 일반 @RequestMapping보다 @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping 같은 method-specific shortcut을 쓰는 것이 자연스럽다. 같은 element에 여러 @RequestMapping 계열을 함께 두면 첫 번째만 사용되고 warning이 남는다. 프로젝트 규칙: - controller 클래스에는 resource base path를 둔다 - endpoint 메서드에는 HTTP method-specific mapping을 사용한다 - endpoint 메서드에 일반 @RequestMapping을 남발하지 않는다 - 한 메서드에 중복 mapping annotation을 두지 않는다 ### 4.2 매핑 조건은 필요한 만큼만 사용한다 Spring은 mapping에 URL, HTTP method, params, headers, media types를 모두 조건으로 줄 수 있다. 하지만 이 프로젝트에서는 매핑 조건을 너무 많이 걸어 endpoint를 읽기 어렵게 만드는 것을 지양한다. 꼭 필요한 경우에만 consumes, produces, params, headers를 사용한다. 프로젝트 규칙: - 기본은 path + HTTP method - content-type이 중요한 endpoint만 consumes - response media type이 실제로 갈리는 endpoint만 produces - params/headers 조건은 routing necessity가 분명할 때만 사용 ### 4.3 endpoint path는 리소스 중심으로 둔다 이 항목은 주로 Practice + Project Recommendation 이다. 프로젝트 규칙: - path는 동사보다 리소스 중심으로 설계한다 - action semantics는 가능한 한 HTTP method로 표현한다 - 도메인적으로 특별한 command endpoint가 필요하면 예외적으로 명시적 action path를 사용할 수 있다 - controller 이름, class path, method path가 함께 읽혔을 때 자원이 자연스럽게 보이게 한다 ## 5. 메서드 시그니처 표준 ### 5.1 인자는 명시적으로 바인딩한다 Spring MVC controller method는 매우 다양한 인자를 지원한다. HttpServletRequest, HttpServletResponse, WebRequest, HttpSession, @PathVariable, @RequestParam, @RequestHeader, @ModelAttribute, @RequestBody 등 여러 타입과 annotation을 사용할 수 있다. 그러나 지원된다고 해서 아무 인자나 다 쓰는 방향을 기본값으로 두지 않는다. 프로젝트 규칙: - path 값은 @PathVariable - query 값은 @RequestParam - header 값은 @RequestHeader - JSON body는 @RequestBody - form/query binding object는 @ModelAttribute - 각 입력의 출처가 메서드 시그니처에 드러나야 한다 ### 5.2 request DTO를 명시적으로 사용한다 Spring 공식 문서 기준으로 @ModelAttribute는 request parameters, URI path variables, headers를 model object에 바인딩하며, 보안상 웹 바인딩 전용 객체를 쓰거나 constructor binding only를 고려하고, property binding이 필요하면 allowedFields를 제한하라고 권장한다. 즉, 웹 입력용 객체 설계는 신중해야 한다. 프로젝트 규칙: - JSON body는 전용 request DTO에 받는다 - form/query binding도 가능하면 전용 request model로 받는다 - entity/domain object를 직접 @RequestBody / @ModelAttribute 대상으로 쓰지 않는다 - request DTO는 transport model이지 domain model이 아니다 ### 5.3 Servlet API 의존은 정말 필요할 때만 허용한다 Spring MVC는 HttpServletRequest, HttpServletResponse, WebRequest 등 직접적인 request/response 접근을 지원한다. 하지만 API controller의 기본 시그니처는 annotation 기반 인자 바인딩으로 충분해야 한다. 프로젝트 규칙: - request body, path, query, header는 annotation 기반 인자 바인딩을 우선한다 - HttpServletRequest / HttpServletResponse는 다음처럼 정말 필요한 경우에만 사용한다 - request attribute 접근 - low-level header 처리 - cookie 직접 제어 - file streaming / low-level response control - 단순 입력 추출 때문에 Servlet API를 들고 오지 않는다 ### 5.4 변환과 포맷팅은 ad-hoc parsing보다 binder/converter를 우선 검토한다 Spring 공식 문서 기준으로 @InitBinder나 전역 MVC config를 통해 Converter, Formatter, PropertyEditor 등을 등록해 타입 변환과 formatting을 구성할 수 있다. controller 안에서 문자열 파싱 로직을 계속 반복하는 것보다 framework extension point를 쓰는 편이 낫다. 프로젝트 규칙: - 동일한 문자열 → 타입 변환이 반복되면 converter/formatter를 검토한다 - web binding 전용 커스터마이징은 @InitBinder 또는 전역 conversion 설정으로 뺀다 - controller 본문에 파싱 로직을 반복해서 쓰지 않는다 ## 6. 반환값 표준 ### 6.1 기본 반환은 body 중심이다 Spring MVC는 @ResponseBody, ResponseEntity, HttpHeaders, ProblemDetail, String view name 등 다양한 반환형을 지원한다. API controller에서는 body 중심 반환이 기본이다. @RestController는 class level @ResponseBody semantics를 제공한다. 프로젝트 규칙: - 일반 API 성공 응답은 ApiResult 또는 프로젝트 표준 response DTO를 반환한다 - view name 반환은 API controller에서 사용하지 않는다 - Map 같은 임시 응답은 승인 후보 코드에서 지양한다 ### 6.2 ResponseEntity는 “정말 HTTP 응답을 제어해야 할 때” 사용한다 Spring 공식 문서 기준으로 ResponseEntity는 전체 응답, 즉 HTTP status, headers, body를 함께 지정하는 반환형이다. 따라서 모든 endpoint에서 습관적으로 ResponseEntity를 쓸 필요는 없고, HTTP 응답 제어가 필요한 경우에 의미가 있다. 프로젝트 규칙: - 기본 성공 200 응답이고 header 제어가 없다면 굳이 ResponseEntity를 강제하지 않는다 - 다음 경우에는 ResponseEntity를 사용한다 - 201 Created + Location - 204 No Content - header 제어 - 캐시/조건부 응답 - 파일 다운로드 - endpoint별로 status가 의미 있게 달라지는 경우 ### 6.3 응답 body 공통화는 controller보다 advice에서 해결할 수 있다 Spring의 ResponseBodyAdvice는 @ResponseBody 또는 ResponseEntity controller method의 body가 HttpMessageConverter로 쓰이기 직전에 응답을 커스터마이징할 수 있고, @ControllerAdvice로 등록하면 자동 적용된다. 따라서 전역 ApiResult 래핑이나 공통 필드 보강은 controller 메서드마다 반복하지 않고 advice에 둘 수 있다. 프로젝트 규칙: - 응답 envelope 공통화 정책이 있으면 controller 반복보다 advice를 우선 검토한다 - 다만 controller가 이미 명시적으로 ApiResult를 반환하는 프로젝트라면 이중 래핑을 피한다 ## 7. 예외, 검증, 인증 경계 ### 7.1 controller는 예외를 직접 처리하는 기본 위치가 아니다 Spring 공식 문서 기준으로 @ExceptionHandler, @InitBinder, @ModelAttribute는 local controller에도 둘 수 있고, @ControllerAdvice / @RestControllerAdvice로 전역 적용도 가능하다. 전역 @ExceptionHandler는 local handler 뒤에 적용된다. 프로젝트 규칙: - 일반적인 API 예외 처리는 @RestControllerAdvice에 둔다 - controller 메서드 안에서 try-catch로 공통 예외 응답을 반복해서 만들지 않는다 - controller local @ExceptionHandler는 endpoint-local 정책이 정말 필요할 때만 사용한다 ### 7.2 controller의 검증 책임은 request boundary까지다 이 항목은 앞서 정리한 validation 문서와 연결되는 Official + Practice + Project Recommendation 이다. 프로젝트 규칙: - controller는 request DTO / scalar input의 구조 검증을 트리거한다 - business rule 검증, 상태 조회 기반 검증, 불변식 보장은 application/domain에서 담당한다 - controller validation 통과를 domain correctness의 보장으로 간주하지 않는다 ### 7.3 인증 주체 접근은 명시적이고 제한적으로 한다 Spring MVC는 다양한 method argument를 허용하므로 인증 객체도 여러 방식으로 전달될 수 있다. 이 프로젝트에서는 인증 객체 접근을 controller 시그니처에서 명시적으로 드러내되, security implementation 세부사항이 controller 전체에 번지지 않게 한다. 프로젝트 규칙: - 인증 주체는 가능한 한 전용 principal / auth object / argument resolver 결과로 받는다 - controller가 security framework의 저수준 타입에 과도하게 묶이지 않게 한다 - 인증 객체 접근 표준은 이후 Authentication Object Access Standard 문서에서 더 구체화한다 ## 8. 의존성 및 금지 규칙 이 항목은 주로 Project Recommendation 이다. 프로젝트 규칙: - controller는 repository를 직접 호출하지 않는다 - controller는 EntityManager, JDBC template, external client를 직접 조립하지 않는다 - controller에 @Transactional을 기본 금지한다 - controller는 domain entity를 외부 응답 모델로 직접 반환하지 않는다 - controller에서 event 발행, retry, async orchestration을 직접 수행하지 않는다 - controller 메서드가 “HTTP boundary 번역” 이상으로 커지기 시작하면 application service / mapper / advisor / resolver 분리를 검토한다 ## 9. 체크리스트 다음 질문에 “예”로 답할 수 있어야 한다. - 이 클래스는 REST API endpoint이므로 @RestController가 자연스러운가? - class-level path와 method-level HTTP mapping이 명확한가? - 입력 출처가 메서드 시그니처에서 드러나는가? - request DTO와 domain/entity가 분리되어 있는가? - controller가 repository/transaction/business rule을 직접 품고 있지 않은가? - 응답 형식이 프로젝트 표준(ApiResult 등)에 맞는가? - 예외 처리와 공통 응답 보강을 advice 쪽으로 밀어냈는가?