Develop

28일차_Spring MVC(동작원리, 처리흐름), Logback, Lombok 본문

백엔드/KDT_Programmers

28일차_Spring MVC(동작원리, 처리흐름), Logback, Lombok

230801 2025. 4. 11. 02:19

안녕하세요!

오늘은 데브코스 28일차 (6주 4일차) 입니다.

 

Spring MVC 에서 컨트롤러랑 뷰정도 다뤄봤고, 로깅 프레임워크 Logback과 어노테이션 프레임워크 Lombok 을 설치했습니다.

 

수업중엔 무슨말인지 이해를 못했는데, 정리하다보니 대충 구조가 이해됩니다.

 

MVC 는 Model, View, Controller 로 이루어진 디자인 패턴이고,

클라이언트의 요청은 Controller가 받아서 처리하고,
필요한 비즈니스 로직은 Model에서 수행한 뒤,
그 결과를 View가 응답을 받아서 클라이언트에게 보여준다.

 

 

 


 

Spring MVC 동작원리 (기본적인 요청 처리 흐름)

(클라이언트)
  |
  |   HTTP 요청
  ↓
┌───────────────────┐
│ DispatcherServlet │
└───────────────────┘
  |
  ↓
┌─────────────────────┐
│    Handler Mapping  │──> 요청 URI에 따라 핸들러(컨트롤러 메서드)를 찾아냄
└─────────────────────┘
  |
  ↓
┌────────────────────┐
│  Interceptors      │──> 요청을 가로채거나 전처리, 후처리
└────────────────────┘
  |
  ↓
┌─────────────────┐
│    Controller   │──> 비즈니스 로직 수행, Model에 데이터 저장, 논리 뷰* 이름 반환
└─────────────────┘                             (* 논리 뷰 : 컨트롤러에서 반환하는 이름
  |                                               'return welcome;'에서 welcome임)
  ↓
┌────────────────────┐
│  View Resolver     │──> 논리 뷰 이름을 실제 물리적 뷰* 리소스로 변환
└────────────────────┘                   (* 물리 뷰 : 리소스 파일 경로    
  |                                       'WEB-INF/views/welcome.jsp`)
  ↓
┌───────────────────────┐
│       View Rendering  │──> Model 데이터를 사용하여 최종 응답 생성
└───────────────────────┘
  |
  ↓
(클라이언트)
  |
  |←─ HTTP 응답 ──

  1. 요청(HTTP): 클라이언트로부터 HTTP 요청을 받음
  2. DispatcherServlet: 요청을 받아 핸들러를 매핑하고 적절한 컨트롤러 메서드를 호출
  3. Interceptors: 요청의 전처리/후처리를 수행(필수적이지 않음)
  4. Controller: 비즈니스 로직을 처리하거나 뷰로 데이터를 전달
  5. View Resolver: 뷰 이름을 물리적 뷰 리소스로 변환
  6. View Rendering: 최종 응답 생성(JSP, JSON 등)

 

 

 

위에서 학습한 내용을 오늘 공부한 내용에서 어떻게 적용되는지 알아봤습니다.

Spring MVC 기반 웹 애플리케이션의 전체 아키텍처 흐름

 

1. Request

 

[Client → Server 데이터 전송 방식]

  • application/x-www-form-urlencoded
    • 기본 form 전송방식 (key = value)
  • multipart/form-data
    • 파일 업로드용 (enctype="multipart/form-data")
  • application/json
    • REST API에서 주로 쓰는 포맷
    • 클라이언트가 JSON 보내면 스프링이 Jackson으로 파싱

 

[요청 처리 Annotation]

  • @GetMapping/ @PostMapping : HTTP 메서드 매핑
  • @PathVariable : URL 경로 변수
  • @RequestParam : 쿼리 파라미터 매핑 (우리는 안 씀)
  • @ModelAttribute :
    • form 데이터 바인딩
    • 매개변수 2개 이상부터는 명시적으로 사용
  • @SessionAttribute , @HttpSession : 세션 접근
  • @CookieValue : 쿠키 접근
  • /redirect/... : 리다이렉트 처리
  • forward : 내부 요청 전달

 

2. DispatcherServlet (Front Controller)

  • 모든 요청을 받아서 적절한 컨트롤러로 전달
  • 반환 결과를 ViewResolver 로 넘겨주거나, JSON 처리

 

3. Controller

  • 요청 파라미터 바인딩
    • 암묵적 바인딩 : 필드명 자동 매칭
    • 명시적 바인딩 : @RequestParam, @PathVariable, @ModelAttribute
    • urlEncodedBinder : 스프링 내부 자동 처리
  • 검증
    • @Valid, BindingResult : 유효성 검증
    • @InitBinder : 커스텀 바인딩 설정 가능
  • 예외처리
    • @ControllerAdvice , @ExceptionHandler : 전역 예외 핸들링
    • SweetAlert2 : alert 으로 사용자에게 알려줌
  • 응답
    • @RestController : JSON 응답 (REST 전용, 내부에 @ResponseBody 포함)
    • @ResponseBody : 객체 → JSON 변환
    • @JsonFormat : JSON 응답 포맷 설정 → Spring은 Jackson 사용 (Gson 아님)

 

4. Service

  • @Service : 서비스 선언 (비즈니스 로직 담당)
  • DTO → 페이로드 DTO로 가공 (민감정보 제거 목적)
  • 검증 로직도 이곳에서 처리
  • Validator Bean 직접 주입 X (순환참조 위험)

 

5. Model and View

  • JSP : 동적 HTML 뷰
  • REST API : JSON 응답 (@RestController 이용)

 

6. Response

  • JSP : ViewResolver 를 통해 .jsp 반환
  • REST API : JSON 변환 후 응답

 

7. 기타

  • Logback
    • 공식문서: https://logback.qos.ch/
    • logback.xml 설정:
      • 로그 레벨 INFO로 조정
      • 콘솔 외에 로그 파일로 내보내기
      • 언어 영어로 설정
  • SLF4J : 로그 표준 라이브러리
    • ConsoleAppender, RollingFileAppender 등으로 로그 출력
    •  System.out 대신 사용 (레벨별 로그, 코드 비용 ↓)
  • Lombok
    • 공식문서 : https://projectlombok.org/features/
    • @RequiredArgsConstructor : final 필드만 포함한 생성자
    • @AllArgsConstructor / @NoArgsConstructor : 모든 필드 / 파라미터 없는 생성자
    • @Getter, @Setter, @ToString 등 코드 줄여줌
    • @Builder : 빌더 패턴 자동 생성 (사용 전 annotation enable 필요)
  • 컨트롤러에서 response body 타입 안 맞으면 문제 생김
    • JSON 반환 시 @ResponseBody 또는 @RestController 명시

 

 


오늘의 질문

Spring MVC 에서의 Principal 의 의미

  • 역할
    • Principal 클래스는 클라이언트(사용자)의 신원 정보를 포함하는 Java 객체
    • Spring MVC는 Principal 을 사용하여 사용자 인증 상태를 관리하고,
      이 정보는 로그인 시 세션에 저장되며 인증이 필요한 요청을 처리할때 사용함
  • 특징
    • 불변객체이며, Principal 의 정보는 보통 세션 내에 저장되기 때문에, 멀티 스레드 환경에서도 안전하게 사용 가능
    • 신원 확인: 클라이언트의 신원 확인
    • 접근 제어: 특정 자원 또는 기능에 대한 접근을 제어
    • 속성: getName() 메서드를 통해 사용자 이름을 반환

 

OffsetDateTime 과 LocalDateTime 의 차이점

 

Offset이란?

UTC 세계 표준시 (혹은 그리니치 평균시, GMT) 특정 지역의 표준시 사이의 시간차이를 나타내는 값으로,
이 차이는 시간대(Time Zone)와 서머타임 등으로 발생할 수 있으며, 한국은 UTC 보다 9시간 빠르기 때문에 + 9:00 오프셋을 가짐

 

OffsetDateTime

  1. 용도
    • 데이터베이스, 네트워크 프로토콜에서 날짜/시간 데이터를 정확히 전달
    • 오프셋 정보를 포함하여 지역 시간을 명확히 표현
  2. 구조
    • 날짜와 시간 필드(연도, 월, 일, 시, 분, 초, 나노초)와 UTC/Greenwich로부터의 오프셋 정보를 저장
    • 예: 2025-04-10T22:21:00+09:00 (한국 표준시 기준).
  3. Immutable 및 Thread-Safe
    • OffsetDateTime 객체는 생성 후 변경할 수 없으며, 멀티스레드 환경에서도 안전하게 사용할 수 있습니다.
  4. Instant와 ZonedDateTime과의 관계
    • Instant: UTC 기준의 특정 시점을 표현.
    • OffsetDateTime: UTC 오프셋을 추가하여 지역 날짜 및 시간을 표현.
    • ZonedDateTime: 시간대 규칙까지 포함한 날짜 및 시간 표현.

예제

  • 현재 시간 가져오기
OffsetDateTime now = OffsetDateTime.now(); System.out.println("현재 OffsetDateTime: " + now);

 

  • 직접 값 지정
ZoneOffset offset = ZoneOffset.of("+09:00"); OffsetDateTime dateTime = OffsetDateTime.of(2025, 4, 10, 22, 21, 0, 0, offset); System.out.println("지정된 OffsetDateTime: " + dateTime);

 

 

LocalDateTime

  1. 용도
    • 날짜와 시간 데이터를 간단하게 표현 (지역 ,시간대 고려x)
      • 시간대 변환 또는 오프셋 포함 작업에는 zonedDateTime 혹은 OffsetDateTime 을 사용
  2. 구조
    • 연도, 월(1-12), 일, 시, 분, 초, 나노초를 포함하며, 무시할 수 있는 모든 필드에 대해 기본값을 제공
    • 예: 2023-04-10T21:25:00
  3. Immutable 및 Thread-Safe
    • LocalDateTime 객체는 생성 후 변경할 수 없으며, 멀티스레드 환경에서도 안전하게 사용 가능
    • 모든 메서드는 새 인스턴스를 반환

 

예제

  • 현재 시간 가져오기
LocalDateTime now = LocalDateTime.now();
System.out.println("현재 LocalDateTime: " + now);
  • 직접 값 지정
LocalDateTime custom = LocalDateTime.of(2025, 4, 10, 22, 50, 59);

 

Logback (Logging Framework)

Logback

  • Java 애플리케이션에서 로그 메시지를 기록하는데 사용되는 로깅 프레임워크

 

로그 설정

  • logback.xml 생성
    • appender , logger, root logger 설정
<?xml version="1.0" encoding="UTF-8"?>

<!-- appender -->
<configuration>
  <property name="LOG_PATTERN"
    value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${LOG_PATTERN}</pattern>
    </encoder>
  </appender>

  <!-- root logger -->
  <root level="debug">
    <appender-ref ref="CONSOLE"/>
  </root>
  
	<!-- logger -->
  <logger name="com.grepp.spring" level="DEBUG" additivity="false">
    <appender-ref ref="CONSOLE"/>
  </logger>

  <logger name="org.apache.ibatis.logging.jdbc.PreparedStatementLogger" level="INFO" additivity="false">
    <appender-ref ref="CONSOLE"/>
  </logger>
</configuration>

 

  • 로그 사용 (lombok)
    • import lombok.extern.slf4j.Slf4j;
    • @slf4j
@RestController
@RequestMapping("api")
@Slf4j
public class RestApiController {

    @GetMapping("test") 
    public RestPayload test(RestForm form) {
        log.info("form: {}", form);
        OffsetDateTime now = OffsetDateTime.now();
        return new RestPayload(1, "aaa@aaa.com", now, now.toEpochSecond());
    }
}

 

Log Level

  • 종류
    1. TRACE: 가장 상세한 정보(복잡한 디버깅용)
    2. DEBUG: 상세 정보(개발 단계 디버깅용)
    3. INFO: 일반적인 실행 흐름 및 정상 상태 정보(운영 모니터링용)
    4. WARN: 잠재적 문제나 경고(운영 시 주의 필요)
    5. ERROR: 심각한 오류와 시스템 장애(운영 시 즉시 조치 필요)
  • 특징
    • 설정된 레벨 이상의 로그를 출력 (INFO 설정 → INFO, WARN, ERROR 출력)
    • 운영 환경에서는 INFO나 WARN 이상을 사용하고, 개발 환경에서는 DEBUG를 주로 사용

 

 

하하하 오늘도 수고하셨습니다... 지금은 새벽 2시 17분 ...그래도 조금 이해되서 기분좋다~

바이바이