티스토리 뷰

Spring

스프링 키워드 모음 #2

chaewonni 2024. 4. 16. 03:26

1.  데이터의 멱등성

수학에서의 멱등성은 "f(f(x)) = f(x) 일 때, f(x)는 멱등하다."고 한다.

 

이와 같이 데이터의 멱등성이란 동일한 작업을 여러 번 수행하더라도 결과가 동일하게 유지되는 성질을 말한다. 즉, 같은 요청을 여러 번 반복해도 최종 결과는 최초의 한 번 수행한 결과와 차이가 없어야 한다는 것이다. 이는 데이터베이스 API 디자인, 함수형 프로그래밍 등 여러 분야에서 중요한 개념으로 활용된다.

 

HTTP 메서드와 멱등성

GET

 

GET 메서드는 서버에서 데이터를 조회하는 메서드이다. 이는 데이터베이스의 변경 없이 순수하게 정보를 가져오는 동작이므로, 여러 번 수행해도 결과가 동일하기에 '멱등성을 가진다'고 할 수 있다.

 

요청

GET /users

 

응답

{
  "users": [
    {
      "id": 1,
      "name": "김땡땡",
      "email": "kim@naver.com"
    },
    {
      "id": 2,
      "name": "이땡땡",
      "email": "lee@naver.com"
    },
    {
      "id": 3,
      "name": "박땡땡",
      "email": "park@naver.com"
    }
  ]
}

 

이 경우, 동일한 'GET /users' 요청을 여러 번 보내도 응답으로 반환되는 사용자 목록은 변하지 않는다. 서버의 데이터 상태를 변경하지 않으므로, 요청을 여러 번 반복해도 결과가 동일하게 유지된다.


POST

POST 메서드는 새로운 데이터를 서버에 생성하는 메서드이다. 이는 데이터베이스 내에 새로운 항목을 추가하게 된다. 

같은 요청을 여러 번 보내면 동일한 데이터의 복사본이 여러 개 생성되므로, 각각의 요청마다 서버의 상태가 변경된다. 

즉 '멱등성을 가지고 있지 않다'고 할 수 있다. 

 

요청

POST /posts
{
  "title": "새 글",
  "content": "내용입니다."
}

 

응답

{
  "id": 101,
  "title": "새 글",
  "content": "내용입니다."
}

 

'POST' 요청을 반복하면, 서버에는 "새 글"이라는 제목과 "내용입니다."라는 본문을 가진 게시글이 여러 개 생성된다.


PUT

PUT 메서드는 서버의 특정 리소스를 새 데이터로 완전히 대체하는 메서드이다. 동일한 PUT 요청을 여러 번 수행해도 서버 상태는 첫 번째 요청 후 변경되지 않기 때문에 '멱등성을 가진다'고 할 수 있다. 

 

// 김땡떙의 현재 정보
{
  "id": 1,
  "name": "김땡땡",
  "email": "old_kim@naver.com"
}

 

요청

PUT /users/1
{
  "id": 1,
  "name": "김땡땡",
  "email": "new_kim@naver.com"
}

 

응답

{
  "id": 1,
  "name": "김땡땡",
  "email": "new_kim@naver.com"
}

 

위의 PUT 요청이 처음 실행될 때, 서버에서는 "김땡땡"의 이메일이 "old_kim@example.com"에서 "new_kim@example.com"으로 변경된다. 이후 동일한 PUT 요청을 여러 번 반복하더라도 "김땡땡"의 정보는 이미 "new_kim@example.com"으로 변경되어 있기 때문에 추가적인 변화는 일어나지 않는다. 결과적으로, 서버의 상태는 첫 번째 요청 이후 변하지 않으므로, 이 PUT 요청은 멱등하다고 할 수 있다.

 

++)  PUT 메서드는 일부 값만 요청한 경우엔 지정하지 않은 값들은 null 혹은 default 값으로 대체되는데, 특별한 매커니즘에 의해 기존 값이 유지될 수도 있다고 한다.

그래도 전체 값을 변경할 땐 PUT, 일부 값만을 변경할 때는 PATCH를 사용하도록 하자!


PATCH

PATCH 메서드는 리소스의 전체를 교체하는 'PUT' 메서드와 달리, 리소스의 일부분만을 수정하는 데 사용되는 메서드이다.

PATCH 메서드는 일반적으로 '멱등성을 가지고 있지 않다'고 간주되는 경우가 많으나, 특정 상황에서는 멱등성을 가질 수도 있다

 

멱등한 경우

//김땡떙의 현재 정보
{
  "id": 1,
  "name": "김땡땡",
  "email": "kim@naver.com"
}

 

요청

PATCH /users/1
{
  "email": "updated_kim@naver.com"
}

 

응답

{
  "id": 1,
  "name": "김땡땡",
  "email": "updated_kim@naver.com"
}

 

이와 같이 동일한 요청을 반복하면, 이메일 주소는 "updated_kim@example.com"로 이미 설정되어 있으므로, 추가적인 변경은 없다. 결과적으로 각 요청의 응답은 동일하다.

 

멱등하지 않은 경우

//김땡땡의 현재 정보
{
  "id": 1,
  "name": "김땡땡",
  "email": "kim@naver.com",
  "update_count": 0
}

 

요청

PATCH /users/1
{
  "email": "updated_kim@naver.com"
}

 

응답

{
  "id": 1,
  "name": "김땡땡",
  "email": "updated_kim@naver.com",
  "update_count": 1
}
{
  "id": 1,
  "name": "김땡땡",
  "email": "updated_kim@naver.com",
  "update_count": 2
}

 

이 경우, 서버 상태가 각 요청마다 변경되므로 이 'PATCH' 요청은 멱등하지 않다.


DELETE

DELETE 메서드는 서버의 특정 리소스를 삭제하는 메서드이다. 

리소스를 한 번 삭제한 후 같은 DELETE 요청을 다시 보내도 이미 삭제된 리소스는 영향을 받지 않기 때문에, DELETE 메서드는 '멱등성을 가지고 있다'고 할 수 있다.

 

요청

DELETE /posts/101

 

응답

200 OK

 

(첫 번째 요청이 성공하여 특정 리소스가 삭제된 후 다시 DELETE 요청을 보낼 때)

404 NOT FOUND

2.  Spring Boot 구동 원리 

Spring Boot 애플리케이션을 시작할 때 일련의 과정이 순차적으로 실행된다.

package org.sopt.practice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PracticeApplication {

	public static void main(String[] args) {
		SpringApplication.run(PracticeApplication.class, args);
	}

}

 

1) 애플리케이션 시작: 'main()' 메서드에서 'SpringApplication.run()'을 호출한다. 이는 Spring Boot 애플리케이션의 진입점이다.

 

2) 환경 설정 로드: 애플리케이션의 구성 파일(application.properties, application.yml 등)과 클래스패스를 검색하여 필요한 환경 설정을 로드한다.

 

3) Bean 등록: '@SpringBootApplication' 어노테이션과 함께 자동 설정(auto-configuration)이 활성화된다. 이 어노테이션은 '@SpringBootConfiguration', '@EnableAutoConfiguration', '@ComponentScan'을 포함하고 있어서 Spring이 자동으로 Bean을 찾아 등록하고 필요한 구성을 적용할 수 있게 한다.

사진과 같이 @SpringBootApplication은  어노테이션은 '@SpringBootConfiguration', '@EnableAutoConfiguration', '@ComponentScan'을 포함하고 있다.

 

각각을 자세히 알아보면,

 

- @SpringBootConfiguration

  • @SpringBootConfiguration은 Spring의 @Configuration을 더 구체화한 것으로, Spring Boot 애플리케이션의 설정을 정의하는 클래스임을 나타낸다. @Configuration 어노테이션이 붙은 클래스는 스프링 컨테이너에 의해 Bean 정의들을 등록하는 데 사용된다.
  • 이 어노테이션은 구성 클래스에서 Bean 메소드를 호출할 때 CGLIB를 통해 프록시를 생성하는 방식을 사용한다. 이는 한 애플리케이션 컨텍스트 내에서 @Bean 메소드 간에 호출이 발생할 때 항상 싱글톤을 보장한다.

 

- @EnableAutoConfiguration

  • @EnableAutoConfiguration은 Spring Boot의 자동 구성 매커니즘을 활성화한다. 이 어노테이션은 Spring Boot가 클래스패스에서 조건에 따라 Bean을 추가하고 다양한 설정을 자동으로 구성할 수 있게 해준다. @EnableAutoConfiguration은 내부적으로'AutoConfigurationImportSelector'를 사용하여 조건에 맞는 @Configuration 클래스들을 자동으로 등록한다. 예를 들어, spring-boot-starter-jpa가 클래스패스에 있으면 Spring Data JPA를 구성하는 Bean들이 자동으로 등록된다.

 

- @ComponentScan

  • @ComponentScan은 스프링이 다른 컴포넌트, 설정, 서비스, 필요한 Bean들을 자동으로 찾아서 스프링 애플리케이션 컨텍스트에 등록할 수 있도록 지정한다. 이 어노테이션은 지정된 패키지 경로 내의 클래스들을 스캔하여 @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스들을 찾아 스프링 컨테이너에 Bean으로 등록한다.

 

4) 서버 실행: 내장된 서블릿 컨테이너(Tomcat 등)가 시작되고, HTTP 요청을 처리할 준비를 한다.

Dispatcher Servlet의 구동은 바로 이 서버 실행 단계에서 주로 이루어진다. 내장 서블릿 컨테이너가 시작되면서, Spring Boot는 자동 설정을 통해 DispatcherServlet을 구성하고 활성화한다.


Dispatcher Servlet

Servlet(서블릿)이란? 

 

먼저 서블릿이란 웹 서버의 기능을 확장하여 동적인 웹 페이지를 생성하기 위한 자바 프로그래밍 기술이다. 웹 페이지에 동적인 내용이 필요해지면서, 정적 자료만으로는 부족한 부분을 보완하기 위해 개발되었다. 서블릿은 웹 어플리케이션 서버(WAS)의 서블릿 컨테이너에서 실행되며, HTTP 요청을 받아들여 그에 맞는 비즈니스 로직을 처리하고 결과를 HTTP 응답으로 반환한다. 이 과정을 통해 서블릿은 사용자의 요구에 따라 실시간으로 페이지를 생성하고 업데이트하는 데 핵심적인 역할을 한다. 

 

Dispatcher Servlet(디스패처 서블릿)이란? 

그렇다면 디스패처 서블릿은 무엇일까? 디스패처 서블릿은 Spring MVC의 핵심 구성 요소로, 모든 웹 요청을 중앙에서 관리하는 프론트 컨트롤러(Front Controller) 패턴의 구현체이다. 일반적인 서블릿과는 달리, 디스패처 서블릿의 주요 목적은 웹 요청을 Spring의 다양한 컨트롤러에 적절히 분배하는 것이다. 

 

먼저, 클라이언트로부터의 요청을 톰캣과 같은 서블릿 컨테이너가 처음으로 받는다.

이후 모든 요청은 프론트 컨트롤러(서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아 처리하는 컨트롤러) 패턴의 구현체라는 이름에 알맞게, 디스패처 서블릿에 의해 가장 먼저 처리된다.

디스패처 서블릿은 요청에 대한 공통적인 작업을 수행한 후, 해당 요청을 처리할 적절한 컨트롤러를 찾아 해당 작업을 위임한다. 

 

Dispatcher Servlet의 동작과정

  1. 요청 수신: 클라이언트로부터 오는 모든 HTTP 요청은 디스패처 서블릿이 처음으로 받는다.
  2. Handler Mapping 조회: Dispatcher Servlet은 요청 URL을 기반으로 적절한 핸들러(컨트롤러)를 찾기 위해 Handler Mapping을 조회한다.
  3. Handler Adapter 호출: Handler Mapping이 결정한 핸들러를 실행하기 위해, Dispatcher Servlet은 해당 핸들러를 실행할 수 있는 Handler Adapter를 호출한다.
  4. 컨트롤러 실행: Handler Adapter는 요청을 처리하기 위한 컨트롤러의 메서드를 실행한다. 이 컨트롤러는 보통 @RestController 어노테이션이 붙어 REST API 요청을 처리하는 역할을 한다.
  5. 비즈니스 로직 실행: 컨트롤러는 필요한 비즈니스 로직을 수행하기 위해 서비스 계층을 호출한다. 서비스 계층은 비즈니스 로직과 데이터 접근을 분리하여, 데이터베이스나 다른 서비스와의 상호작용을 담당하는 리포지토리를 사용한다.
  6. 응답 생성: 컨트롤러는 비즈니스 로직의 결과를 바탕으로 HTTP 응답을 생성하며, 이를 Response Entity 객체에 담아 반환한다.
  7. Handler Adapter로부터 응답 반환: Handler Adapter는 컨트롤러가 생성한 Response Entity를 다시 Dispatcher Servlet에 반환한다.
  8. 클라이언트에게 응답 전송: Dispatcher Servlet은 최종적으로 생성된 응답을 클라이언트에게 전송한다.

이 과정을 통해, Dispatcher Servlet은 Spring MVC에서 HTTP 요청의 처리부터 응답 생성 및 반환까지의 전체적인 흐름을 제어하게 된다. 클라이언트는 웹 브라우저, 모바일 앱 등 다양한 소스에서 올 수 있으며, Dispatcher Servlet은 이러한 다양한 요청을 적절하게 처리하기 위한 중앙 집중식 입구의 역할을 한다.


3.  EnumType.ORDINAL의 단점

'EnumType.ORDINAL'과 'EnumType.STRING'은 Java의 Enum 타입을 데이터베이스에 저장할 때 사용되는 JPA 어노테이션 설정이다. 스프링 프레임워크와 JPA를 사용하여 데이터베이스 엔티티를 모델링할 때, 열거형(Enum) 필드가 있으면 이 두 옵션 중 하나를 선택해야 한다.

 

그렇다면 EnumType.STRING에 비해 EnumType.ORDINAL이 가지는 단점은 무엇일까?

EnumType.ORDINAL 설정은 열거형의 각 상수가 선언된 순서에 따라 데이터베이스에 정수로 저장된다.

예를 들어

public enum Status {
    OPEN, REVIEW, CLOSED
}
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;

@Entity
public class Task {
    @Id
    private Long id;

    @Enumerated(EnumType.ORDINAL)
    private Status status;
}

 

이렇게 Enum 타입과 엔티티를 정의했다면, 'OPEN'은 0, 'REVIEW'는 1, 'CLOSED'는 2로 데이터베이스에 저장된다.

 

단점 1. 순서 의존성

위와 같이 데이터베이스에 저장했다가,

만약 Enum 상수의 순서를 OPEN, CLOSED, REVIEW 순으로 변경하거나, OPEN과 REVIEW 사이에 새로운 상수 'RESERVE'가 추가된다면 기존에 저장된 데이터의 의미가 완전히 달라지게 된다.

 

단점 2. 가독성 저하

데이터베이스에서 값을 조회할 때 정수 값만 보고는 어떤 상태를 의미하는지 직관적으로 이해하기가 어렵다.

 

결론

따라서 데이터베이스에서 값이 문자열로 저장되어 데이터를 조회할 때 바로 어떤 상태를 의미하는지 알 수 있으며, Enum 상수의 순서를 변경하거나 새로운 상수를 추가/삭제해도 데이터베이스에 저장된 기존 데이터에 영향을 미치지 않는 EnumType.STRING을 사용하는 것이 안전하다.


4.  스프링 빈의 생명주기 

스프링의 IoC(Inversion of Control) 컨테이너는 빈(Bean) 객체들의 생명주기를 관리하며, 이는 객체의 생성부터 소멸까지를 포함한다. 이러한 관리 과정에서 스프링 컨테이너는 의존성 주입을 통해 객체 간의 관계를 설정하고, 객체의 생명주기에 맞춰 적절한 시점에 초기화와 소멸 작업을 자동으로 수행한다.

 

스프링 빈의 라이프 사이클은 다음과 같다.

  • 1. 스프링 컨테이너 생성 
  • 2. 스프링 빈 등록
  • 3. 의존관계 주입
  • 4. 초기화 콜백
  • 5. 사용
  • 6. 소멸 전 콜백
  • 7. 스프링 종료  

각각을 살펴 보면,

1. 스프링 컨테이너 생성

스프링 컨테이너(ApplicationContext)는 스프링 애플리케이션의 중심이 되는 부분으로, 빈의 생명주기를 관리하고 의존성을 주입하는 등의 역할을 수행한다. 이 컨테이너는 애플리케이션 시작 시 생성되며, 다음과 같은 과정을 통해 이루어진다.

  • 구성 파일 로드: 컨테이너는 XML, Java Config 파일이나 클래스를 로드하여 빈의 정의와 의존성 정보를 읽는다.
  • 컨테이너 초기화: 읽어들인 설정을 바탕으로 컨테이너가 초기화되며, 이 과정에서 빈의 정의에 따라 빈들이 등록된다.

2. 스프링 빈 등록

빈은 스프링 컨테이너에 의해 관리되는 객체로, 컨테이너에 빈을 등록하는 방법으로는 2가지가 있다.

  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바 코드로 직접 스프링 빈 등록

 

컴포넌트 스캔과 자동 의존관계 설정 방법은 클래스 선언부 위에 @Component 어노테이션을 사용하면 된다.

스프링 프레임워크에서 @Controller, @Service, @Repository는 모두 @Component 어노테이션을 포함하고 있다. 따라서 이 어노테이션이 붙은 클래스들은 스프링 컨테이너에 의해 자동으로 인스턴스화되며 스프링 빈으로 등록된다. 이는 개발자가 스프링의 종속성 주입 기능을 통해 쉽게 관리할 수 있게 해준다.

package com.example.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
}

package com.example.repository;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
}

 

자바 코드로 직접 스프링 빈 등록 방법은 수동으로 자바 설정 클래스를 만들어야 한다. 클래스를 만들고 클래스 선언부에 '@Configuration' 어노테이션을 추가하면, 이 어노테이션이 붙은 클래스는 스프링의 설정 정보를 제공하는 역할을 하게 된다.

설정 클래스 내에서는 하나 이상의 메서드에 '@Bean' 어노테이션을 붙여 해당 메서드가 생성하는 객체를 스프링 빈으로 등록할 수 있다. 이 메서드들은 특정 타입의 객체를 반환하며, 이 객체들은 스프링 컨테이너에 의해 관리된다.

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.example.service.UserService;
import com.example.repository.UserRepository;

@Configuration
public class AppConfig {
    // UserRepository 빈 등록
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    // UserService 빈 등록, 의존성 주입
    @Bean
    public UserService userService(UserRepository userRepository) {
        return new UserService(userRepository);
    }
}

 


3. 의존관계 주입

스프링은 빈 생성 후, 빈 간 정의된 의존성을 주입하는 과정을 관리한다.

의존관계를 주입하는 방법에는

  • 자동 주입 (Autowiring)
  • 명시적 주입 (Explicit Injection)

이 있다.

 

자동 주입 (Autowiring)

스프링의 @Autowired 어노테이션을 사용하는 자동 주입 방법은 스프링이 설정된 규칙에 따라 필요한 의존성을 자동으로 빈에 주입한다. 스프링은 주입할 객체의 타입, 이름, 혹은 다른 어노테이션을 기반으로 적절한 빈을 찾아 필드, 생성자, 또는 세터 메소드에 주입한다.

 

명시적 주입 (Explicit Injection)

명시적 주입은 개발자가 코드 내에서 직접 의존성을 지정하는 방식입니다. 이 방식은 주로 생성자 주입, 세터 주입을 포함한다.

  • 생성자 주입: 이 방식은 클래스의 생성자를 사용하여 의존성을 주입한다. 스프링 4.3 이상에서는 단일 생성자가 있는 경우 @Autowired 어노테이션을 생략할 수 있으며, 이는 빈의 불변성을 보장하고 필수 의존성을 명시적으로 제공한다. 참고로 생성자 주입은 객체의 생성과 의존 관계 주입이 동시에 일어난다.
  • 세터 주입: 메소드(보통 세터)를 통해 의존성을 주입한다. 이 방식은 선택적 의존성에 유용하며, 빈의 상태를 필요에 따라 변경할 수 있다.
  • 필드 주입: 필드 주입은 @Autowired 필드에 직접 적용하는 방식으로, 간단하게 의존성을 주입할 있다. 하지만, 방법은 테스트가 어렵고 코드의 깔끔함을 저해할 있다.

4. 초기화 콜백

  • @PostConstruct
  • InitializingBean
  • init-method

@PostConstruct 어노테이션

@PostConstruct는 자바 표준(JSR-250) 어노테이션으로, 빈의 모든 의존성이 주입된 바로 직후에 실행된다. 이 어노테이션을 메소드에 적용하면, 스프링 컨테이너가 빈을 생성하고 의존성 주입이 완료된 후에 해당 메소드를 자동으로 호출한다. 이 방법은 추가적인 설정 없이 간단히 초기화 작업을 수행할 수 있어 매우 편리하다.

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class SimpleService {
    @PostConstruct
    public void init() {
        // 초기화 로직
        System.out.println("SimpleService is initialized");
    }
}

 

InitializingBean 인터페이스

InitializingBean은 스프링에서 제공하는 인터페이스로, 빈의 초기화 로직을 직접 제어하고 싶을 때 사용할 수 있다. 이 인터페이스를 구현하고 afterPropertiesSet() 메소드를 오버라이드하면, 스프링 컨테이너가 의존성 주입을 완료한 직후에 이 메소드를 호출한다. 이 방법은 프로그래밍 방식으로 더 세밀한 제어가 필요한 경우 유용하다.

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class AdvancedService implements InitializingBean {
    @Override
    public void afterPropertiesSet() {
        // 초기화 로직
        System.out.println("AdvancedService is initialized");
    }
}

 

init-method 설정

initMethod는 스프링 빈이 생성되고 의존성 주입이 완료된 후에 호출되는 초기화 메소드를 지정한다. 이 방법은 빈이 사용 준비를 하기 전에 필요한 설정이나 상태를 확인하는 로직을 실행하는 데 유용하다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean(initMethod = "initialize")
    public DataProcessor dataProcessor() {
        return new DataProcessor();
    }
}

public class DataProcessor {
    public void initialize() {
        // 필요한 초기화 로직
        System.out.println("DataProcessor is ready to process data!");
    }
}

5. 사용

초기화가 완료된 빈은 애플리케이션에서 필요로 할 때 활성화되어 사용된다. 빈은 서비스를 제공하거나 데이터 처리를 수행하는 등 다양한 역할을 한다.


6. 소멸 전 콜백

  • @PreDestroy
  • DisposableBean
  • destroy-method

@PreDestory 어노테이션

@PreDestroy는 자바 표준 어노테이션으로, 빈이 제거되기 직전에 필요한 정리 작업을 수행하기 위해 사용된다. 리소스 해제, 네트워크 연결 해제 등의 작업을 수행하는 데 이상적이다.

import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class NetworkClient {
    @PreDestroy
    public void disconnect() {
        // 네트워크 연결 해제
        System.out.println("Network connection is closed");
    }
}

 

DisposableBean 인터페이스

DisposableBean은 스프링 프레임워크가 제공하는 인터페이스로, 빈의 소멸 로직을 제어할 때 사용된다. destroy() 메소드를 구현하여 소멸 직전에 실행되어야 할 로직을 명시할 수 있다.

import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;

@Component
public class ResourceHandler implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        // 리소스 해제 로직
        System.out.println("Releasing resources");
    }
}

 

destroy-method 설정

destroyMethod는 스프링 컨테이너가 종료되면서 빈이 소멸될 때 호출되는 메소드를 지정한다. 이 설정은 주로 자원 해제, 연결 종료 등의 정리 작업을 안전하게 수행하기 위해 사용된다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean(destroyMethod = "close")
    public ResourceHandler resourceHandler() {
        return new ResourceHandler();
    }
}

public class ResourceHandler {
    public void close() {
        // 리소스 해제 로직
        System.out.println("Resources are cleaned up!");
    }
}

7. 스프링 종료  

스프링 컨테이너가 종료되면서 애플리케이션 내 모든 빈들이 소멸된다. 이 과정에서 모든 리소스가 안전하게 해제되고, 연결된 모든 서비스가 정리된다. 스프링 애플리케이션이 전체적으로 안전하게 종료되며, 이는 애플리케이션의 생명주기를 효과적으로 관리하는 데 필수적인 부분이다.


5.  @RequiredArgsConstructor

@RequiredArgsConstructor 어노테이션은 클래스 내에서 최종(final) 또는 @NonNull로 선언된 모든 필드에 대해 생성자를 자동으로 생성해 준다. 이는 주입할 필요가 있는 의존성을 가진 필드에 대해 생성자 주입을 사용할 때 특히 유용하며, 생성자 코드를 직접 작성하지 않아도 되기 때문에 코드의 간결성을 유지할 수 있다.

 

@RequiredArgsConstructor 주요 특징

  • 생성자 자동 생성: 클래스에 명시적인 생성자가 없을 경우, Lombok은 final 또는 @NonNull 어노테이션이 붙은 모든 필드를 초기화하는 생성자를 자동으로 생성한다.
  • 의존성 주입 용이: 스프링 같은 DI(Dependency Injection) 프레임워크와 함께 사용할 때, 이 생성자를 통해 의존성 주입이 자동으로 이루어진다.
  • 코드 간결성: 생성자 코드를 직접 작성할 필요 없이 의존성 관리가 가능하여, 클래스의 코드를 간결하게 유지할 있다.

@RequiredArgsConstructor 없는 경우

import org.springframework.stereotype.Service;

@Service
public class UserService {
    private final UserRepository userRepository;

    // 생성자를 직접 작성
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void processUser(Long userId) {
        User user = userRepository.findById(userId);
        System.out.println("Processing user: " + user.getName());
    }
}

 

@RequiredArgsConstructor 있 경우

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    // Lombok이 생성자를 자동으로 생성
    public void processUser(Long userId) {
        User user = userRepository.findById(userId);
        System.out.println("Processing user: " + user.getName());
    }
}

 


6.  @PathVariable

'@PathVariable'은 Spring MVC에서 URI 경로에서 특정 부분을 추출하여 메소드의 매개변수로 전달하는 데 사용하는 어노테이션이다. 이를 통해 RESTful 웹 서비스에서 동적으로 변하는 URI 요소를 쉽게 처리할 수 있다. @PathVariable은 주로 REST API에서 리소스를 식별하는 데 사용되며, URI 경로의 일부를 컨트롤러의 핸들러 메소드 매개변수로 매핑하는 역할을 한다.

 

@PathVariable 어노테이션은 메소드 매개변수 앞에 선언하여 해당 매개변수가 URI의 변수 부분임을 지정한다. Spring MVC는 URI에서 해당 변수의 값을 추출하여 메소드 매개변수에 자동으로 바인딩한다.

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping("/users/{userId}")
    public String getUserById(@PathVariable String userId) {
        // 실제 애플리케이션에서는 userId를 사용하여 데이터베이스에서 사용자 정보를 조회할 것입니다.
        return "User info for user ID: " + userId;
    }
}

 

위의 코드처럼 @PathVariable을 사용하여 사용자 ID를 URL 경로의 일부로 받아 해당 사용자의 정보를 반환하고 있다.

 

+ ) 변수 이름과 메소드 매개변수 이름이 다를 경우

@GetMapping("/users/{id}")
public String getUserById(@PathVariable("id") String userId) {
    return "User info for user ID: " + userId;
}

 

@PathVariable을 사용할 때 변수 이름과 메소드 매개변수 이름이 다를 경우, 어노테이션의 value 속성을 사용하여 매핑할 변수의 이름을 명시적으로 지정할 수 있다.


7. @Transactional

@Transactional 어노테이션은 스프링 프레임워크의 선언적 트랜잭션 관리 기능을 제공한다. 이 어노테이션을 사용하면, 스프링 컨테이너가 메소드 실행을 트랜잭션의 일부로 자동으로 처리하게 된다. @Transactional은 특정 메소드 또는 클래스 전체에 적용할 수 있으며, 이를 통해 데이터베이스 트랜잭션의 시작, 커밋, 롤백을 자동으로 관리할 수 있다.

 

@Transactional을 메소드나 클래스에 적용하면, 해당 범위 내의 데이터베이스 작업이 하나의 트랜잭션으로 처리된다. 트랜잭션이 성공적으로 완료되면 자동으로 커밋되고, 예외가 발생하면 롤백된다.

@Transactional
public String createMember(MemberCreateDto memberCreate) {
    // Member 엔티티를 DTO로부터 생성
    Member member = Member.create(memberCreate.name(), memberCreate.part(), memberCreate.age());

    // 생성된 Member 엔티티를 데이터베이스에 저장
    memberRepository.save(member);

    // 저장된 Member의 ID를 문자열로 반환
    return member.getId().toString();
}

 

위 예시에선 '@Transactional' 어노테이션이 메소드에 적용되어 있으므로, 이 메소드는 하나의 트랜잭션으로 처리된다. 메소드 실행 시작 시 트랜잭션이 시작되고, 메소드 실행이 성공적으로 완료되면 트랜잭션이 커밋된다. 만약 실행 중 예외가 발생하면 트랜잭션이 롤백된다.

또한 '@Transactional'이 있게 되면서 데이터베이스 작업 중 발생할 수 있는 문제로부터 데이터 일관성을 보호할 수 있다. 예를 들어, Member 정보를 저장하는 과정에서 오류가 발생하면, 이미 수행된 변경사항들(여기서는 Member 생성)은 롤백되어 데이터베이스의 상태가 이전 상태로 복원된다.

 

+) 고급 사용법

@Transactional은 다양한 속성을 지원하여 트랜잭션의 동작 방식을 세밀하게 제어할 수 있다.

  • readOnly: 트랜잭션을 읽기 전용으로 설정할 때 사용한다. 데이터를 변경하지 않는 작업에서 성능 최적화를 위해 사용된다.
  • timeout: 트랜잭션이 지정된 시간 내에 완료되지 않으면 시스템이 롤백하도록 설정한다.
  • isolation: 트랜잭션의 격리 수준을 설정한다. 격리 수준에 따라 동시성을 관리할 수 있다.
  • rollbackFor: 지정한 예외 유형에 대해서 롤백을 수행한다.
  • noRollbackFor: 지정한 예외 유형에 대해서는 롤백을 수행하지 않는다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함