본문 바로가기
Spring MVC

[MVC] 스프링 MVC - 기본 기능(1)

by 개발 Blog 2025. 2. 23.

공부 내용을 정리하고 앞으로의 학습에 이해를 돕기 위해 작성합니다.

 

프로젝트 생성

스프링 부트 프로젝트를 생성할 때 Packaging은 War가 아니라 Jar를 선택해야 한다.
이는 JSP를 사용하지 않는 환경에서 권장되는 방식이며, 내장 서버(Tomcat 등)와 함께 실행하는 데 최적화되어 있다.

Jar vs War

  • Jar: 내장 서버를 사용하며 webapp 경로를 사용하지 않는다. 최근 대부분의 프로젝트에서 이 방식을 사용한다.
  • War: 내장 서버도 가능하지만, 주로 외부 서버(WebLogic, JBoss 등)에 배포하는 용도로 사용된다.

Welcome 페이지 만들기

이번 장에서 학습할 내용을 편리하게 참고하기 위해 Welcome 페이지를 생성한다.
스프링 부트에서는 /resources/static/ 위치에 index.html 파일을 두면 자동으로 Welcome 페이지로 처리된다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
    <li>로그 출력
        <ul>
            <li><a href="/log-test">로그 테스트</a></li>
        </ul>
    </li>
    <!-- -->
    <li>요청 매핑
        <ul>
            <li><a href="/hello-basic">hello-basic</a></li>
            <li><a href="/mapping-get-v1">HTTP 메서드 매핑</a></li>
            <li><a href="/mapping-get-v2">HTTP 메서드 매핑 축약</a></li>
            <li><a href="/mapping/userA">경로 변수</a></li>
            <li><a href="/mapping/users/userA/orders/100">경로 변수 다중</a></li>
            <li><a href="/mapping-param?mode=debug">특정 파라미터 조건 매핑</a></li>
            <li><a href="/mapping-header">특정 헤더 조건 매핑(POST MAN 필요)</a></li>
            <li><a href="/mapping-consume">미디어 타입 조건 매핑 Content-Type(POST
                MAN 필요)</a></li>
            <li><a href="/mapping-produce">미디어 타입 조건 매핑 Accept(POST MAN 필
                요)</a></li>
        </ul>
    </li>
    <li>요청 매핑 - API 예시
        <ul>
            <li>POST MAN 필요</li>
        </ul>
    </li>
    <li>HTTP 요청 기본
        <ul>
            <li><a href="/headers">기본, 헤더 조회</a></li>
        </ul>
    </li>
    <li>HTTP 요청 파라미터
        <ul>
            <li><a href="/request-param-v1?username=hello&age=20">요청 파라미터 v1</
                a></li>
            <li><a href="/request-param-v2?username=hello&age=20">요청 파라미터 v2</
                a></li>
            <li><a href="/request-param-v3?username=hello&age=20">요청 파라미터 v3</
                a></li>
            <li><a href="/request-param-v4?username=hello&age=20">요청 파라미터 v4</
                a></li>
            <li><a href="/request-param-required?username=hello&age=20">요청 파라미
                터 필수</a></li>
            기본 값</a></li>
    <li><a href="/request-param-default?username=hello&age=20">요청 파라미터
        <li><a href="/request-param-map?username=hello&age=20">요청 파라미터
            MAP</a></li>
        <li><a href="/model-attribute-v1?username=hello&age=20">요청 파라미터
            @ModelAttribute v1</a></li>
        <li><a href="/model-attribute-v2?username=hello&age=20">요청 파라미터
            @ModelAttribute v2</a></li>
</ul>
</li>
<li>HTTP 요청 메시지
    <ul>
        <li>POST MAN</li>
    </ul>
</li>
<li>HTTP 응답 - 정적 리소스, 뷰 템플릿
    <ul>
        <li><a href="/basic/hello-form.html">정적 리소스</a></li>
        <li><a href="/response-view-v1">뷰 템플릿 v1</a></li>
        <li><a href="/response-view-v2">뷰 템플릿 v2</a></li>
    </ul>
</li>
<li>HTTP 응답 - HTTP API, 메시지 바디에 직접 입력
    <ul>
        <li><a href="/response-body-string-v1">HTTP API String v1</a></li>
        <li><a href="/response-body-string-v2">HTTP API String v2</a></li>
        <li><a href="/response-body-string-v3">HTTP API String v3</a></li>
        <li><a href="/response-body-json-v1">HTTP API Json v1</a></li>
        <li><a href="/response-body-json-v2">HTTP API Json v2</a></li>
    </ul>
</li>
</ul>
</body>
</html>

로깅 간단히 알아보기

운영 시스템에서는 System.out.println()을 사용하여 콘솔에 직접 로그를 출력하는 대신, 별도의 로깅 라이브러리를 사용한다.
로깅은 프로그램의 실행 흐름을 파악하고, 문제 발생 시 디버깅을 용이하게 하며, 성능 최적화에 중요한 역할을 한다.

스프링 부트에서는 기본적으로 Logback을 사용하며, 로깅 라이브러리를 통합하는 SLF4J 인터페이스를 지원한다.

 

로깅 라이브러리

스프링 부트에서 제공하는 기본 로깅 라이브러리는 다음과 같다.

라이브러리 설명
SLF4J 여러 로깅 라이브러리를 통합하는 인터페이스
Logback 스프링 부트의 기본 로깅 라이브러리
Log4J, Log4J2 과거에 많이 사용된 로깅 프레임워크

 

SLF4J는 인터페이스 역할을 하며, 실제 구현체로 Logback을 기본적으로 사용한다.
실무에서는 대부분 Logback을 그대로 사용한다.

로그 선언 방법

1. 일반적인 로그 선언

private final Logger log = LoggerFactory.getLogger(getClass());

또는

private static final Logger log = LoggerFactory.getLogger(클래스명.class);
 

2. Lombok을 사용하여 간단하게 선언

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SampleClass {
    public void testLog() {
        log.info("로그 출력 테스트");
    }
}

→ @Slf4j 애노테이션을 사용하면 Logger 객체를 직접 선언하지 않아도 된다.

로그 호출 방법

1. 일반적인 로그 출력

log.info("hello");

 

2. System.out.println()과 비교

System.out.println("hello"); // 콘솔 출력
log.info("hello"); // 로깅 프레임워크를 통한 출력

System.out.println()은 단순히 콘솔에 출력하는 반면, 로깅을 사용하면 다양한 설정과 출력을 조절할 수 있다.

 

로그 레벨 (Log Level)

로그는 여러 레벨을 지원하며, TRACE > DEBUG > INFO > WARN > ERROR 순으로 중요도가 높아진다.

레벨  설명
TRACE 가장 상세한 디버깅 정보를 제공 (개발 시 사용)
DEBUG 디버깅 정보를 출력 (개발 시 주로 사용)
INFO 일반적인 시스템 실행 흐름 (운영 서버 기본)
WARN 경고 메시지 (문제가 될 가능성이 있는 경우)
ERROR 치명적인 오류 (예외 발생 등)

 

로그 레벨 설정 예시

  • 개발 서버 → DEBUG 이상 로그 출력
  • 운영 서버 → INFO 이상 로그 출력

로그 사용 예제 (LogTestController)

package hello.springmvc.basic;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class LogTestController {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @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);

        // 잘못된 로그 사용 예시 (문자열 연산 발생)
        log.debug("String concat log=" + name); 

        return "ok";
    }
}

 

@RestController vs @Controller

애노테이션 설명
@Controller 반환 값이 String이면 뷰 이름으로 인식하여 뷰를 렌더링
@RestController 반환 값을 HTTP 메시지 바디에 직접 입력 (@ResponseBody 포함)

→ @RestController를 사용하면 실행 결과로 "ok" 메시지를 직접 받을 수 있다.

 

로그 레벨 설정 변경 (application.properties)

# 전체 로그 레벨 설정 (기본값: info)
logging.level.root=info

# 특정 패키지의 로그 레벨 설정
logging.level.hello.springmvc=debug

→ 특정 패키지(hello.springmvc)의 로그 레벨을 debug로 변경하면,
해당 패키지에서 DEBUG 이상의 로그가 출력된다.

 

올바른 로그 사용법

log.debug("data=" + data);  // ❌ 잘못된 예시
  • INFO 레벨로 설정되어 있어도 문자열 연산("data=" + data)이 실행된다.
  • 불필요한 성능 저하 발생 가능
log.debug("data={}", data); // ✅ 올바른 예시
  • INFO 레벨에서는 아무 일도 발생하지 않음
  • 불필요한 연산 없이 필요할 때만 로그 출력

로그 사용의 장점

1. 추가 정보 제공

  • 쓰레드 정보, 클래스 이름, 로그 레벨 등 추가 정보를 포함할 수 있음

2. 로그 레벨 조절 가능

  • 개발 환경과 운영 환경에 따라 필요한 로그만 출력 가능

3. 다양한 출력 위치 지원

  • 콘솔뿐만 아니라 파일, 네트워크, 원격 서버 등에 로그 저장 가능

4. 성능 최적화

  • System.out.println()보다 내부 버퍼링 및 멀티쓰레드 환경에서 성능이 우수함

더 공부하고 싶다면?