[MVC-5] 스프링 MVC 기본 기능

2025. 12. 30. 19:31·Spring/MVC

 

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 로깅의 장점

  1. 부가 정보 제공: 쓰레드 정보, 클래스명, 시간 정보 등
  2. 로그 레벨 조절: 상황에 맞게 출력 조절 가능
  3. 다양한 출력 대상: 콘솔, 파일, 네트워크 등
  4. 성능 최적화: 버퍼링, 비동기 처리 등
  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 요청 데이터 전달 방식

  1. GET - 쿼리 파라미터
    • URL에 ?key=value&key2=value2 형식
    • 검색, 필터링, 페이징에 사용
  2. POST - HTML Form
    • Content-Type: application/x-www-form-urlencoded
    • 메시지 바디에 쿼리 파라미터 형식
    • 회원 가입, 상품 주문에 사용
  3. 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 동작 원리

  1. HelloData 객체 생성
  2. 요청 파라미터 이름으로 프로퍼티 탐색
  3. 프로퍼티 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 응답 데이터 생성 방법

  1. 정적 리소스: HTML, CSS, JS 파일 직접 제공
  2. 뷰 템플릿: 동적 HTML 생성 (Thymeleaf, JSP)
  3. 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 요청 데이터 읽기

  1. HTTP 요청 도착, 컨트롤러에서 @RequestBody, HttpEntity 파라미터 사용
  2. 메시지 컨버터의 canRead() 호출
    • 대상 클래스 타입 지원 확인
    • Content-Type 미디어 타입 지원 확인
  3. canRead() 조건 만족 시 read() 호출하여 객체 생성

6.3.2 HTTP 응답 데이터 생성

  1. 컨트롤러에서 @ResponseBody, HttpEntity 값 반환
  2. 메시지 컨버터의 canWrite() 호출
    • 대상 클래스 타입 지원 확인
    • Accept 헤더(또는 produces) 미디어 타입 지원 확인
  3. 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 동작 방식

  1. supportsParameter()로 파라미터 지원 여부 확인
  2. 지원하면 resolveArgument()로 실제 객체 생성
  3. 생성된 객체를 컨트롤러에 전달

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 학습 내용 요약

  1. 프로젝트 설정: 스프링 부트를 활용한 MVC 프로젝트 구성
  2. 로깅 시스템: SLF4J와 Logback을 통한 효율적인 로그 관리
  3. 요청 매핑: 다양한 URL 매핑 기법과 조건부 매핑
  4. 요청 데이터: 쿼리 파라미터, 폼 데이터, JSON 데이터 처리
  5. 응답 데이터: 정적 리소스, 뷰 템플릿, HTTP API 응답
  6. 메시지 컨버터: HTTP 메시지 변환 메커니즘
  7. 내부 구조: ArgumentResolver와 ReturnValueHandler 동작 원리

8.2 실무 적용 가이드라인

  1. 로깅: 항상 로깅 라이브러리 사용, System.out.println() 금지
  2. RESTful API: HTTP 메서드와 상태 코드 적절히 활용
  3. 데이터 바인딩: @ModelAttribute로 객체 바인딩, @RequestBody로 JSON 처리
  4. 응답 처리: 상황에 맞게 ResponseEntity, @ResponseBody, @RestController 선택
  5. 확장성: 필요 시 WebMvcConfigurer를 통한 기능 확장

8.3 성능 고려사항

  1. 로깅: 문자열 연결 연산 방지, 매개변수화된 로그 사용
  2. 메시지 컨버터: 적절한 컨텐트 타입 지정으로 불필요한 변환 방지
  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
'Spring/MVC' 카테고리의 다른 글
  • [MVC-6] CSR 환경에서 @RequestBody만 쓸까? (feat. 파일 업로드와 @ModelAttribute)
  • [MVC-4] 스프링 MVC 시작하기: 애노테이션 기반 컨트롤러
  • [MVC-3] Spring MVC 구조 이해
  • [MVC-2] 직접 만드는 MVC 프레임워크
h6bro
h6bro
백엔드 개발자의 기술 블로그
  • h6bro
    Jun's Tech Blog
    h6bro
  • 전체
    오늘
    어제
    • 분류 전체보기 (250) N
      • Java (18)
        • Core (9)
        • Design Pattern (9)
      • Spring (80)
        • Core (24)
        • MVC (6)
        • DB (10)
        • JPA (26)
        • Monitoring (3)
        • Security (11)
        • WebSocket (0)
      • Database (33)
        • Redis (15)
        • MySQL (18)
      • MSA (25) N
        • MSA 기본 (11)
        • MSA 아키텍처 (14) N
      • Kafka (30)
        • Core (18)
        • Connect (12)
      • ElasticSearch (11)
        • Search (11)
        • Logging (0)
      • Test (4)
        • k6 (4)
      • Docker (9)
      • CI&CD (10)
        • GitHub Actions (6)
        • ArgoCD (4)
      • Kubernetes (18)
        • Core (12)
        • Ops (6)
      • Cloud Engineering (4)
        • AWS Infrastructure (3)
        • AWS EKS (1)
        • Terraform (0)
      • Project (8)
        • LinkFolio (1)
        • Secondhand Market (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • Cloud Engineering 포스팅 정리
  • 인기 글

  • 태그

    ㅈ
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
h6bro
[MVC-5] 스프링 MVC 기본 기능
상단으로

티스토리툴바