1. 프로젝트 생성 및 구조
1.1 스프링 부트 프로젝트 생성
스프링 MVC를 학습하기 위한 프로젝트는 스프링 부트 스타터 사이트(https://start.spring.io)를 통해 생성한다. 다음과 같은 설정이 권장된다:
1.1.1 프로젝트 기본 설정
- Project: Gradle Project
- Language: Java
- Spring Boot: 2.4.x 이상
- Project Metadata:
- Group: hello
- Artifact: springmvc
- Name: springmvc
- Package name: hello.springmvc
- Packaging: Jar (주의)
- Java: 11
1.1.2 의존성(Dependencies)
- Spring Web
- Thymeleaf
- Lombok
1.1.3 Packaging 선택: Jar vs War
- Jar 선택 이유: JSP를 사용하지 않는 현대적인 웹 애플리케이션에서는 Jar가 권장됨
- Jar 장점:
- 내장 서버(톰캣) 사용 최적화
- webapp 경로 사용하지 않음
- 배포와 실행이 간편
- War 사용 시기: 주로 외부 서버에 배포할 때 사용
1.2 build.gradle 구성
plugins {
id 'org.springframework.boot' version '2.4.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
1.3 Welcome 페이지 구성
스프링 부트는 정적 컨텐츠 위치에 index.html 파일이 있으면 이를 Welcome 페이지로 자동 인식한다:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC 학습</title>
</head>
<body>
<h1>Spring MVC 기본 기능 학습</h1>
<ul>
<!-- 학습 항목 링크들 -->
</ul>
</body>
</html>
2. 로깅 시스템
2.1 로깅의 중요성
운영 환경에서는 System.out.println() 대신 전문적인 로깅 라이브러리를 사용해야 한다:
2.1.1 로깅 라이브러리 구조
SLF4J (인터페이스)
↑
Logback (구현체 - 스프링 부트 기본)
2.1.2 SLF4J의 역할
- 다양한 로깅 구현체(Logback, Log4J, Log4J2)를 통합한 인터페이스 제공
- 구현체 변경 시 코드 수정 없이 교체 가능
2.2 로그 사용 방법
2.2.1 기본 로거 선언
public class LogTestController {
private final Logger log = LoggerFactory.getLogger(getClass());
// 또는
private static final Logger log = LoggerFactory.getLogger(LogTestController.class);
}
2.2.2 @Slf4j 롬복 애노테이션
@Slf4j
@RestController
public class LogTestController {
@RequestMapping("/log-test")
public String logTest() {
String name = "Spring";
log.trace("trace log={}", name);
log.debug("debug log={}", name);
log.info("info log={}", name);
log.warn("warn log={}", name);
log.error("error log={}", name);
return "ok";
}
}
2.3 로그 레벨과 설정
2.3.1 로그 레벨 순서
TRACE > DEBUG > INFO > WARN > ERROR
2.3.2 application.properties 설정
# 전체 로그 레벨 설정
logging.level.root=info
# 특정 패키지 로그 레벨 설정
logging.level.hello.springmvc=debug
2.3.3 환경별 로그 레벨
- 개발 환경: debug 레벨 사용
- 운영 환경: info 레벨 사용
2.4 로그 사용법
2.4.1 올바른 사용 (권장)
log.debug("data={}", data);
- 로그 레벨이 info일 경우 연산 발생하지 않음
- 매개변수 평가 지연(lazy evaluation) 적용
2.4.2 잘못된 사용 (비권장)
log.debug("data=" + data);
- 로그 레벨과 관계없이 문자열 연결 연산 발생
- 불필요한 성능 저하 초래
2.5 로깅의 장점
- 부가 정보 제공: 쓰레드 정보, 클래스명, 시간 정보 등
- 로그 레벨 조절: 상황에 맞게 출력 조절 가능
- 다양한 출력 대상: 콘솔, 파일, 네트워크 등
- 성능 최적화: 버퍼링, 비동기 처리 등
- 로그 분할: 일별, 용량별 분할 가능
2.6 @RestController vs @Controller
2.6.1 @Controller 동작 방식
- 반환 값이 String이면 뷰 이름으로 인식
- 뷰 리졸버를 통해 해당 뷰를 찾고 렌더링
2.6.2 @RestController 동작 방식
- @Controller + @ResponseBody 조합
- 반환 값을 HTTP 메시지 바디에 직접 입력
- 뷰 렌더링 과정 생략
3. 요청 매핑
3.1 기본 요청 매핑
@RestController
public class MappingController {
@RequestMapping("/hello-basic")
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
}
3.1.1 특징
- URL 끝에 / 유무 모두 허용 (/hello-basic, /hello-basic/)
- 모든 HTTP 메서드 허용 (GET, POST, PUT 등)
- 다중 URL 매핑 가능: @RequestMapping({"/hello-basic", "/hello-go"})
3.2 HTTP 메서드 매핑
3.2.1 명시적 메서드 지정
@RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
public String mappingGetV1() {
log.info("mappingGetV1");
return "ok";
}
3.2.2 HTTP 메서드 전용 애노테이션
@GetMapping("/mapping-get-v2")
@PostMapping("/mapping-post")
@PutMapping("/mapping-put")
@DeleteMapping("/mapping-delete")
@PatchMapping("/mapping-patch")
3.3 경로 변수(Path Variable)
3.3.1 단일 경로 변수
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
log.info("mappingPath userId={}", data);
return "ok";
}
3.3.2 변수명 생략 가능
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable String userId) {
// 변수명과 파라미터명이 같으면 생략 가능
return "ok";
}
3.3.3 다중 경로 변수
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {
log.info("mappingPath userId={}, orderId={}", userId, orderId);
return "ok";
}
3.4 특정 조건 매핑
3.4.1 파라미터 조건
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
return "ok";
}
- params="mode": mode 파라미터 필수
- params="!mode": mode 파라미터 없어야 함
- params="mode!=debug": mode 값이 debug가 아니어야 함
- params={"mode=debug","data=good"}: 다중 조건
3.4.2 헤더 조건
@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
return "ok";
}
3.4.3 Content-Type 조건 (consumes)
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
return "ok";
}
- consumes="application/json": Content-Type이 application/json이어야 함
- 조건 불만족 시 HTTP 415(Unsupported Media Type) 반환
3.4.4 Accept 조건 (produces)
@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
return "ok";
}
- produces="text/html": Accept 헤더에 text/html 포함해야 함
- 조건 불만족 시 HTTP 406(Not Acceptable) 반환
3.5 API 예시: 회원 관리
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {
@GetMapping // GET /mapping/users
public String users() {
return "get users";
}
@PostMapping // POST /mapping/users
public String addUser() {
return "post user";
}
@GetMapping("/{userId}") // GET /mapping/users/{userId}
public String findUser(@PathVariable String userId) {
return "get userId=" + userId;
}
@PatchMapping("/{userId}") // PATCH /mapping/users/{userId}
public String updateUser(@PathVariable String userId) {
return "update userId=" + userId;
}
@DeleteMapping("/{userId}") // DELETE /mapping/users/{userId}
public String deleteUser(@PathVariable String userId) {
return "delete userId=" + userId;
}
}
4. HTTP 요청 데이터 조회
4.1 HTTP 요청 데이터 전달 방식
- GET - 쿼리 파라미터
- URL에 ?key=value&key2=value2 형식
- 검색, 필터링, 페이징에 사용
- POST - HTML Form
- Content-Type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파라미터 형식
- 회원 가입, 상품 주문에 사용
- HTTP Message Body
- JSON, XML, TEXT 데이터 직접 전송
- HTTP API에서 주로 사용
4.2 기본 헤더 정보 조회
@Slf4j
@RestController
public class RequestHeaderController {
@RequestMapping("/headers")
public String headers(
HttpServletRequest request,
HttpServletResponse response,
HttpMethod httpMethod, // HTTP 메서드 조회
Locale locale, // Locale 정보 조회
@RequestHeader MultiValueMap<String, String> headerMap, // 모든 헤더
@RequestHeader("host") String host, // 특정 헤더
@CookieValue(value = "myCookie", required = false) String cookie // 쿠키
) {
log.info("request={}", request);
log.info("response={}", response);
log.info("httpMethod={}", httpMethod);
log.info("locale={}", locale);
log.info("headerMap={}", headerMap);
log.info("header host={}", host);
log.info("myCookie={}", cookie);
return "ok";
}
}
4.2.1 MultiValueMap 특징
- 하나의 키에 여러 값 저장 가능
- HTTP 헤더, 쿼리 파라미터 처리에 적합
MultiValueMap<String, String> map = new LinkedMultiValueMap();
map.add("keyA", "value1");
map.add("keyA", "value2");
List<String> values = map.get("keyA"); // ["value1", "value2"]
4.3 HTTP 요청 파라미터 - @RequestParam
4.3.1 기본 사용
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
@RequestParam("username") String memberName,
@RequestParam("age") int memberAge) {
return "ok";
}
4.3.2 파라미터명 생략
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(
@RequestParam String username, // HTTP 파라미터명과 변수명 같으면 생략 가능
@RequestParam int age) {
return "ok";
}
4.3.3 @RequestParam 완전 생략
@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {
return "ok";
}
- 단순 타입(String, int, Integer 등)일 경우 생략 가능
- 생략 시 required=false 적용
4.3.4 필수 여부 설정
@ResponseBody
@RequestMapping("/request-param-required")
public String requestParamRequired(
@RequestParam(required = true) String username, // 필수 파라미터
@RequestParam(required = false) Integer age) { // 선택적 파라미터
return "ok";
}
주의사항:
- @RequestParam(required = false) int age: 불가능 (null을 int에 할당할 수 없음)
- @RequestParam(required = false) Integer age: 가능
4.3.5 기본값 설정
@ResponseBody
@RequestMapping("/request-param-default")
public String requestParamDefault(
@RequestParam(required = true, defaultValue = "guest") String username,
@RequestParam(required = false, defaultValue = "-1") int age) {
return "ok";
}
- defaultValue 설정 시 required 의미 없음
- 빈 문자열("")에도 기본값 적용
4.3.6 Map으로 조회
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {
log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
4.4 HTTP 요청 파라미터 - @ModelAttribute
4.4.1 데이터 객체 정의
@Data
public class HelloData {
private String username;
private int age;
}
4.4.2 @ModelAttribute 사용
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
4.4.3 @ModelAttribute 생략
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
return "ok";
}
4.4.4 @ModelAttribute 동작 원리
- HelloData 객체 생성
- 요청 파라미터 이름으로 프로퍼티 탐색
- 프로퍼티 setter 호출하여 값 바인딩
- username 파라미터 → setUsername() 호출
- age 파라미터 → setAge() 호출
4.4.5 바인딩 오류 처리
- age=abc처럼 숫자에 문자 입력 시 BindException 발생
- 검증(Validation) 단계에서 처리
4.5 HTTP 메시지 바디 - 단순 텍스트
4.5.1 InputStream 직접 조회
@PostMapping("/request-body-string-v1")
public void requestBodyString(HttpServletRequest request, HttpServletResponse response)
throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
response.getWriter().write("ok");
}
4.5.2 InputStream 파라미터
@PostMapping("/request-body-string-v2")
public void requestBodyStringV2(InputStream inputStream, Writer responseWriter)
throws IOException {
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
responseWriter.write("ok");
}
4.5.3 HttpEntity 사용
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {
String messageBody = httpEntity.getBody();
return new HttpEntity<>("ok");
}
4.5.4 @RequestBody 사용
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) {
log.info("messageBody={}", messageBody);
return "ok";
}
4.6 HTTP 메시지 바디 - JSON
4.6.1 HttpServletRequest로 직접 처리
@PostMapping("/request-body-json-v1")
public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response)
throws IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
HelloData data = objectMapper.readValue(messageBody, HelloData.class);
response.getWriter().write("ok");
}
4.6.2 @RequestBody와 ObjectMapper 조합
@ResponseBody
@PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
HelloData data = objectMapper.readValue(messageBody, HelloData.class);
return "ok";
}
4.6.3 @RequestBody로 객체 직접 받기
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return "ok";
}
4.6.4 HttpEntity 사용
@ResponseBody
@PostMapping("/request-body-json-v4")
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
HelloData data = httpEntity.getBody();
return "ok";
}
4.6.5 객체 반환
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return data; // JSON으로 자동 변환
}
4.6.6 중요 주의사항
- @RequestBody 생략 불가: 생략 시 @ModelAttribute 적용됨
- Content-Type 필수: application/json이어야 JSON 컨버터 적용
5. HTTP 응답
5.1 응답 데이터 생성 방법
- 정적 리소스: HTML, CSS, JS 파일 직접 제공
- 뷰 템플릿: 동적 HTML 생성 (Thymeleaf, JSP)
- HTTP 메시지: JSON 등 데이터 직접 전송 (HTTP API)
5.2 정적 리소스
5.2.1 정적 리소스 경로
스프링 부트는 다음 디렉토리의 정적 리소스를 자동 제공:
- /static
- /public
- /resources
- /META-INF/resources
5.2.2 기본 경로 구조
src/main/resources/static/basic/hello-form.html
↓
<http://localhost:8080/basic/hello-form.html>
5.3 뷰 템플릿
5.3.1 뷰 템플릿 경로
src/main/resources/templates/response/hello.html
5.3.2 컨트롤러 구현
@Controller
public class ResponseViewController {
// ModelAndView 반환
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1() {
ModelAndView mav = new ModelAndView("response/hello")
.addObject("data", "hello!");
return mav;
}
// 문자열 반환 (뷰 이름)
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model) {
model.addAttribute("data", "hello!!");
return "response/hello";
}
// void 반환 (URL 기반 뷰 매핑 - 비권장)
@RequestMapping("/response/hello")
public void responseViewV3(Model model) {
model.addAttribute("data", "hello!!");
}
}
5.4 HTTP API 응답
5.4.1 문자열 응답
@Controller
public class ResponseBodyController {
// HttpServletResponse 직접 사용
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException {
response.getWriter().write("ok");
}
// ResponseEntity 사용
@GetMapping("/response-body-string-v2")
public ResponseEntity<String> responseBodyV2() {
return new ResponseEntity<>("ok", HttpStatus.OK);
}
// @ResponseBody 사용
@ResponseBody
@GetMapping("/response-body-string-v3")
public String responseBodyV3() {
return "ok";
}
}
5.4.2 JSON 응답
@Controller
public class ResponseBodyController {
// ResponseEntity 사용
@GetMapping("/response-body-json-v1")
public ResponseEntity<HelloData> responseBodyJsonV1() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return new ResponseEntity<>(helloData, HttpStatus.OK);
}
// @ResponseBody + @ResponseStatus 사용
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json-v2")
public HelloData responseBodyJsonV2() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return helloData;
}
}
5.5 @RestController
5.5.1 @RestController 특징
- @Controller + @ResponseBody 조합
- 클래스 내 모든 메서드에 @ResponseBody 적용
- HTTP API 전용 컨트롤러에 적합
@RestController
public class ResponseBodyController {
// 모든 메서드에 자동으로 @ResponseBody 적용됨
}
6. HTTP 메시지 컨버터
6.1 HTTP 메시지 컨버터 역할
HTTP 메시지 컨버터는 다음과 같은 상황에서 동작:
- HTTP 요청: @RequestBody, HttpEntity, RequestEntity
- HTTP 응답: @ResponseBody, HttpEntity, ResponseEntity
6.2 기본 메시지 컨버터
스프링 부트가 제공하는 기본 메시지 컨버터 (우선순위 순):
6.2.1 ByteArrayHttpMessageConverter
- 클래스 타입: byte[]
- 미디어 타입: /*
- 요청: @RequestBody byte[] data
- 응답: @ResponseBody return byte[]
- Content-Type: application/octet-stream
6.2.2 StringHttpMessageConverter
- 클래스 타입: String
- 미디어 타입: /*
- 요청: @RequestBody String data
- 응답: @ResponseBody return "ok"
- Content-Type: text/plain
6.2.3 MappingJackson2HttpMessageConverter
- 클래스 타입: 객체 또는 HashMap
- 미디어 타입: application/json 관련
- 요청: @RequestBody HelloData data
- 응답: @ResponseBody return helloData
- Content-Type: application/json
6.3 메시지 컨버터 선택 과정
6.3.1 HTTP 요청 데이터 읽기
- HTTP 요청 도착, 컨트롤러에서 @RequestBody, HttpEntity 파라미터 사용
- 메시지 컨버터의 canRead() 호출
- 대상 클래스 타입 지원 확인
- Content-Type 미디어 타입 지원 확인
- canRead() 조건 만족 시 read() 호출하여 객체 생성
6.3.2 HTTP 응답 데이터 생성
- 컨트롤러에서 @ResponseBody, HttpEntity 값 반환
- 메시지 컨버터의 canWrite() 호출
- 대상 클래스 타입 지원 확인
- Accept 헤더(또는 produces) 미디어 타입 지원 확인
- canWrite() 조건 만족 시 write() 호출하여 응답 생성
6.4 메시지 컨버터 인터페이스
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
7. 요청 매핑 핸들러 어댑터 구조
7.1 ArgumentResolver
7.1.1 역할
- 컨트롤러 메서드의 파라미터 값을 생성
- 다양한 파라미터 타입 지원 (HttpServletRequest, @RequestParam, @RequestBody 등)
7.1.2 인터페이스
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory)
throws Exception;
}
7.1.3 동작 방식
- supportsParameter()로 파라미터 지원 여부 확인
- 지원하면 resolveArgument()로 실제 객체 생성
- 생성된 객체를 컨트롤러에 전달
7.2 ReturnValueHandler
7.2.1 역할
- 컨트롤러 메서드의 반환 값을 처리
- 다양한 반환 타입 지원 (String, ModelAndView, @ResponseBody, HttpEntity 등)
7.3 HTTP 메시지 컨버터 위치
7.3.1 요청 처리 흐름
HTTP 요청 → DispatcherServlet → ArgumentResolver 호출
↓
HttpMessageConverter (읽기)
↓
컨트롤러 실행
7.3.2 응답 처리 흐름
컨트롤러 실행 → ReturnValueHandler 호출
↓
HttpMessageConverter (쓰기)
↓
HTTP 응답 ← DispatcherServlet
7.3.3 주요 Processor
- @RequestBody, @ResponseBody: RequestResponseBodyMethodProcessor
- HttpEntity: HttpEntityMethodProcessor
7.4 확장 포인트
스프링 MVC는 다음과 같은 인터페이스를 제공하여 기능 확장 가능:
7.4.1 확장 가능 인터페이스
- HandlerMethodArgumentResolver: 파라미터 리졸버 확장
- HandlerMethodReturnValueHandler: 반환 값 핸들러 확장
- HttpMessageConverter: 메시지 컨버터 확장
7.4.2 WebMvcConfigurer를 통한 확장
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// 커스텀 ArgumentResolver 추가
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 커스텀 MessageConverter 추가
}
};
}
8. 정리
8.1 학습 내용 요약
- 프로젝트 설정: 스프링 부트를 활용한 MVC 프로젝트 구성
- 로깅 시스템: SLF4J와 Logback을 통한 효율적인 로그 관리
- 요청 매핑: 다양한 URL 매핑 기법과 조건부 매핑
- 요청 데이터: 쿼리 파라미터, 폼 데이터, JSON 데이터 처리
- 응답 데이터: 정적 리소스, 뷰 템플릿, HTTP API 응답
- 메시지 컨버터: HTTP 메시지 변환 메커니즘
- 내부 구조: ArgumentResolver와 ReturnValueHandler 동작 원리
8.2 실무 적용 가이드라인
- 로깅: 항상 로깅 라이브러리 사용, System.out.println() 금지
- RESTful API: HTTP 메서드와 상태 코드 적절히 활용
- 데이터 바인딩: @ModelAttribute로 객체 바인딩, @RequestBody로 JSON 처리
- 응답 처리: 상황에 맞게 ResponseEntity, @ResponseBody, @RestController 선택
- 확장성: 필요 시 WebMvcConfigurer를 통한 기능 확장
8.3 성능 고려사항
- 로깅: 문자열 연결 연산 방지, 매개변수화된 로그 사용
- 메시지 컨버터: 적절한 컨텐트 타입 지정으로 불필요한 변환 방지
- 객체 생성: 불필요한 객체 생성을 피하고 캐싱 활용
스프링 MVC의 이러한 기본 기능들은 현대적인 웹 애플리케이션 개발에 필요한 대부분의 요구사항을 충족시키며, 확장성 있는 아키텍처를 제공한다. 이러한 기초를 잘 이해하는 것은 복잡한 엔터프라이즈 애플리케이션을 구축하는 데 필수적이다.
'Spring > MVC' 카테고리의 다른 글
| [MVC-6] CSR 환경에서 @RequestBody만 쓸까? (feat. 파일 업로드와 @ModelAttribute) (0) | 2026.02.18 |
|---|---|
| [MVC-4] 스프링 MVC 시작하기: 애노테이션 기반 컨트롤러 (0) | 2025.12.30 |
| [MVC-3] Spring MVC 구조 이해 (0) | 2025.12.30 |
| [MVC-2] 직접 만드는 MVC 프레임워크 (0) | 2025.12.30 |
| [MVC-1] 서블릿에서 JSP까지: 자바 백엔드 웹 기술의 발전과 한계 (0) | 2025.12.30 |
