반응형

Spring Boot 및 일반 Spring 환경에서 외부 서비스 기반 설정 관리 방법

어플리케이션을 구동할 때 필요한 설정값들은 보통 환경변수(Environment Variables)나 파일에 저장하여 관리합니다. 하지만 최근에는 설정값을 Zookeeper, AWS Secrets Manager와 같은 외부 서비스에서 불러오거나, 보안을 강화하기 위해 메모리 내에서만 설정값을 저장하는 방식이 점점 더 선호되고 있습니다. 이러한 요구사항을 충족시키기 위해 Spring Boot와 일반 Spring 환경에서 설정값을 효과적으로 관리하는 방법을 소개합니다.

Spring Boot에서 환경 설정값 관리하기

Spring Boot는 EnvironmentPostProcessor를 활용하여 어플리케이션 시작 시점에 환경 설정값을 동적으로 변경하거나 추가할 수 있습니다. 이를 통해 외부 서비스에서 설정값을 불러와 적용하는 것이 가능합니다.

EnvironmentPostProcessor 활용 예제

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.Map;
import java.util.stream.Collectors;
import java.util.Arrays;
import java.util.List;

public class PriceCalculationEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
    private static final List<String> names = Arrays.asList("config.key1", "config.key2"); // 예시 키 리스트

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        var system = environment.getPropertySources().get(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);

        Map<String, Object> prefixed = names.stream()
            .collect(Collectors.toMap(this::rename, system::getProperty));
        environment.getPropertySources()
            .addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new MapPropertySource("prefixer", prefixed));
    }

    private String rename(String originalName) {
        return "prefix." + originalName; // 원하는 방식으로 키 이름 변경
    }
}

적용 방법

  1. Spring Boot의 spring.factories 파일에 등록

src/main/resources/META-INF/spring.factories 파일에 다음 내용을 추가합니다:

org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.PriceCalculationEnvironmentPostProcessor
  1. 어플리케이션 실행 시 자동으로 적용

Spring Boot는 spring.factories에 등록된 EnvironmentPostProcessor를 자동으로 감지하여 적용합니다.

Spring Boot를 사용하지 않는 환경에서 설정값 관리하기

Spring Boot가 아닌 일반 Spring 환경에서도 설정값을 외부 서비스에서 불러오거나 메모리 내에서 관리할 수 있습니다. 이를 위해 ApplicationContextInitializer를 활용할 수 있습니다.

ApplicationContextInitializer 활용 예제

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.boot.context.properties.bind.Binder;

public class PropertyOverrideContextInitializer 
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        var environment = applicationContext.getEnvironment();
        MyConfigProps configProps = Binder.get(environment)
                                         .bind("my-config", MyConfigProps.class)
                                         .orElse(new MyConfigProps());
        System.out.println(configProps.getHomekey());
        // 추가 설정 로직 구현
    }
}

적용 방법

ApplicationContextInitializer를 적용하는 방법은 여러 가지가 있습니다. 아래에 네 가지 주요 방법을 소개합니다.

  1. web.xml 또는 Java Config에서 contextInitializerClasses 추가

web.xml을 사용하는 경우:

<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>com.example.PropertyOverrideContextInitializer</param-value>
</context-param>

Java Config를 사용하는 경우:

import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;

@ContextConfiguration(initializers = PropertyOverrideContextInitializer.class)
public class AppConfig {
    // Bean 정의 등
}
  1. Spring만 사용하는 경우
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.addInitializer(new PropertyOverrideContextInitializer());
        ctx.register(AppConfiguration.class);
        ctx.refresh();

        JobLauncher launcher = ctx.getBean(JobLauncher.class);
        // 어플리케이션 로직 실행
    }
}
  1. spring.factories 파일을 통한 자동 설정 (Spring Boot)

src/main/resources/META-INF/spring.factories 파일에 다음 내용을 추가합니다:

org.springframework.context.ApplicationContextInitializer=\
com.example.PropertyOverrideContextInitializer
  1. 어플리케이션 실행 시 코드로 추가 (Spring Boot)

SpringApplication을 사용하는 경우:

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

@SpringBootApplication
public class YourApp {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(YourApp.class);
        application.addInitializers(new PropertyOverrideContextInitializer());
        application.run(args);
    }
}

SpringApplicationBuilder을 사용하는 경우:

import org.springframework.boot.builder.SpringApplicationBuilder;

public class YourApp {
    public static void main(String[] args) {
        new SpringApplicationBuilder(YourApp.class)
            .initializers(new PropertyOverrideContextInitializer())
            .run(args);
    }
}

프로퍼티 파일을 통한 설정:

application.properties 또는 application.yml 파일에 다음과 같이 설정합니다:

context.initializer.classes=com.example.PropertyOverrideContextInitializer

결론

외부 서비스에서 설정값을 불러오거나, 보안을 강화하기 위해 메모리 내에서만 설정값을 관리하고자 할 때, Spring Boot와 일반 Spring 환경에서 제공하는 EnvironmentPostProcessor와 ApplicationContextInitializer를 활용하면 효과적으로 설정값을 관리할 수 있습니다. 각 방법은 어플리케이션의 구조와 요구사항에 따라 적절히 선택하여 적용할 수 있으며, 이를 통해 보다 유연하고 안전한 설정 관리가 가능합니다.

참고 자료

  1. Baeldung - Spring Tests: Override Properties
  2. Stack Overflow - How to add custom ApplicationContextInitializer to a Spring Boot application
  3. Stack Overflow - Spring ApplicationContextInitializer and properties
  4. Stack Overflow - ApplicationContextInitializer in a non-web Spring context

이 글이 Spring 기반 어플리케이션에서 설정값을 효과적으로 관리하는 데 도움이 되길 바랍니다. 추가적인 질문이나 의견이 있으시면 댓글로 남겨주세요!

반응형
반응형

스프링 프레임워크에서 ResourceLoader를 활용한 파일 접근

스프링(Spring) 프레임워크를 사용하다 보면 webapps 폴더 내의 파일들에 접근해야 하는 경우가 종종 발생합니다. 예를 들어, 설정 파일이나 정적 자원에 접근해야 할 때가 그렇습니다. 전통적으로는 ServletContext를 이용하여 파일에 접근할 수 있지만, 이 방법은 ServletContext에 의존적이라는 단점이 있습니다. 특히 특정 동작만을 위한 모듈을 추가하거나 테스트 환경에서 유연하게 사용하기 어렵습니다.

스프링에서는 이러한 문제를 해결하기 위해 ResourceLoader를 제공하여 보다 유연하게 파일에 접근할 수 있도록 지원합니다. 이번 포스트에서는 ResourceLoader를 활용한 파일 접근 방법을 살펴보고, 실용적인 예제를 통해 이해를 돕겠습니다.

ResourceLoader를 이용한 파일 접근

ResourceLoader는 스프링의 핵심 인터페이스 중 하나로, 다양한 위치에 있는 리소스(파일, 클래스패스 리소스 등)에 접근할 수 있는 기능을 제공합니다. 이를 통해 ServletContext에 의존하지 않고도 필요한 파일에 접근할 수 있습니다.

예제 코드 설명

아래는 ResourceLoaderAware 인터페이스를 구현하여 ResourceLoader를 주입받고, 이를 통해 webapps 폴더 내의 config.txt 파일을 읽어오는 예제입니다.

import java.io.IOException;
import java.io.InputStream;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

public class ArbitraryResourceLoader implements ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    // 파일을 읽어오는 메소드
    public void readConfigFile() throws IOException {
        // "file:" 접두사를 사용하여 파일 시스템의 절대 경로로 리소스를 로드
        Resource resource = resourceLoader.getResource("file:webapps/config.txt");
        InputStream is = resource.getInputStream();
        try {
            int i = 0;
            while ((i = is.read()) != -1) {
                char c = (char) i;
                System.out.print(c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null)
                is.close();
        }
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

주요 포인트

  1. ResourceLoaderAware 인터페이스 구현: ResourceLoaderAware를 구현하면 스프링 컨테이너가 ResourceLoader를 자동으로 주입해줍니다. 이를 통해 ResourceLoader를 직접 의존하지 않고도 리소스에 접근할 수 있습니다.

  2. 리소스 로드 방식: resourceLoader.getResource("file:webapps/config.txt")에서 "file:" 접두사는 파일 시스템의 절대 경로를 의미합니다. 필요에 따라 클래스패스 리소스나 URL 리소스 등 다양한 방식으로 리소스를 로드할 수 있습니다.

  3. 입출력 처리: InputStream을 사용하여 파일의 내용을 읽어오며, 예외 처리와 자원 해제를 철저히 합니다.

스프링 빈 설정

위에서 작성한 ArbitraryResourceLoader 클래스를 스프링 빈으로 등록하기 위해 XML 설정 파일을 사용합니다. 프로젝트 구조에 맞게 패키지 경로를 수정해야 합니다.

<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- ArbitraryResourceLoader 빈 등록 -->
    <bean id="arbitraryResourceLoader" class="org.test.sample.ArbitraryResourceLoader"/>

</beans>

주요 포인트

빈 등록: <bean> 태그를 사용하여 ArbitraryResourceLoader 클래스를 빈으로 등록합니다. 스프링 컨테이너가 관리하게 되며, 의존성 주입을 통해 ResourceLoader가 자동으로 주입됩니다.

JavaConfig를 이용한 설정

XML 설정 파일 대신 Java 기반 설정을 사용할 수도 있습니다. JavaConfig를 사용하면 더욱 타입 안전하고, IDE의 지원을 받을 수 있습니다.

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

@Configuration
public class AppConfig {

    @Bean
    public ArbitraryResourceLoader arbitraryResourceLoader() {
        return new ArbitraryResourceLoader();
    }
}

주요 포인트

  • @Configuration 어노테이션: 이 클래스가 스프링의 설정 클래스임을 명시합니다.
  • @Bean 어노테이션: 메소드를 통해 빈을 등록합니다. 메소드 이름이 빈의 ID가 됩니다.

실용적인 예제: 설정 파일 로드 및 활용

이제 실제로 ArbitraryResourceLoader를 활용하여 설정 파일을 로드하고, 애플리케이션에서 활용하는 간단한 예제를 살펴보겠습니다.

설정 파일 (config.txt)

먼저 webapps 폴더 내에 config.txt 파일을 생성하고, 다음과 같은 내용을 추가합니다.

database.url=jdbc:mysql://localhost:3306/mydb
database.username=root
database.password=secret

ArbitraryResourceLoader 클래스 수정

설정 파일을 읽어와서 프로퍼티로 저장하는 기능을 추가합니다.

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;

public class ArbitraryResourceLoader implements ResourceLoaderAware {
    private ResourceLoader resourceLoader;
    private Properties properties = new Properties();

    public void loadConfig() throws IOException {
        Resource resource = resourceLoader.getResource("file:webapps/config.txt");
        try (InputStream is = resource.getInputStream()) {
            properties.load(is);
        }
    }

    public String getProperty(String key) {
        return properties.getProperty(key);
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

애플리케이션에서 사용하기

스프링 애플리케이션 컨텍스트를 로드하고, ArbitraryResourceLoader 빈을 사용하여 설정 값을 가져오는 예제입니다.

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
    public static void main(String[] args) {
        // JavaConfig를 사용하는 경우
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // XML 설정을 사용하는 경우
        // ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        ArbitraryResourceLoader loader = context.getBean(ArbitraryResourceLoader.class);

        try {
            loader.loadConfig();
            String dbUrl = loader.getProperty("database.url");
            String dbUser = loader.getProperty("database.username");
            String dbPassword = loader.getProperty("database.password");

            System.out.println("Database URL: " + dbUrl);
            System.out.println("Database User: " + dbUser);
            System.out.println("Database Password: " + dbPassword);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

실행 결과

Database URL: jdbc:mysql://localhost:3306/mydb
Database User: root
Database Password: secret

장점 및 결론

ResourceLoader를 활용하면 ServletContext에 의존하지 않고도 다양한 위치에 있는 리소스에 접근할 수 있어, 모듈의 재사용성과 테스트 용이성이 크게 향상됩니다. 또한, JavaConfig를 사용하면 설정이 더욱 직관적이고 관리하기 쉬워집니다.

스프링의 ResourceLoader는 단순히 파일 접근뿐만 아니라, 클래스패스 리소스, URL 리소스 등 다양한 리소스 타입을 지원하므로, 애플리케이션의 요구사항에 맞게 유연하게 사용할 수 있습니다. 이를 통해 보다 견고하고 유지보수하기 쉬운 애플리케이션을 개발할 수 있습니다.

스프링을 활용한 리소스 접근 방법을 이해하고, 실제 프로젝트에 적용해보세요. 다양한 상황에서 유용하게 사용할 수 있을 것입니다.

반응형
반응형

JPA 테이블 매핑과 어노테이션 가이드

JPA(Java Persistence API)에서 테이블을 정의하는 방법에 대해 자세히 알아보겠습니다. JPA는 자바 애플리케이션에서 관계형 데이터베이스를 객체지향적으로 사용할 수 있게 해주는 기술입니다.

기본 엔티티 매핑

가장 기본적인 엔티티 매핑에는 다음과 같은 어노테이션들이 사용됩니다:

@Entity

클래스가 JPA 엔티티임을 나타냅니다. 이 어노테이션이 적용된 클래스는 데이터베이스 테이블과 매핑됩니다.

@Entity
public class Order {
    // 클래스 내용
}

@Table

엔티티와 매핑할 테이블을 지정합니다. 생략하면 엔티티 이름이 테이블 이름으로 사용됩니다.

@Entity
@Table(name = "ORDERS", 
       uniqueConstraints = {@UniqueConstraint(
           name = "NAME_AGE_UNIQUE",
           columnNames = {"NAME", "AGE"})})
public class Order {
    // 클래스 내용
}

기본 키 매핑

@Id와 @GeneratedValue

기본 키 매핑을 위해 사용되며, 데이터베이스별로 다른 생성 전략을 사용할 수 있습니다:

// MySQL
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// Oracle
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;

// H2
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

필드 매핑

@Column

컬럼 매핑을 세밀하게 제어할 수 있습니다:

@Column(name = "ORDER_DATE",
        nullable = false,
        length = 10,
        updatable = false)
private String orderDate;

@Temporal

날짜 타입 매핑에 사용됩니다:

@Temporal(TemporalType.TIMESTAMP)
private Date orderDate;

@Enumerated

열거형 타입 매핑에 사용됩니다:

@Enumerated(EnumType.STRING)
private OrderStatus status;

연관관계 매핑

@OneToMany

일대다 관계를 매핑합니다:

@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();

@ManyToOne

다대일 관계를 매핑합니다:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;

실전 예제

다음은 주문 시스템의 완전한 엔티티 예제입니다:

@Entity
@Table(name = "ORDERS")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ORDER_ID")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();

    @Temporal(TemporalType.TIMESTAMP)
    private Date orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    // 연관관계 편의 메서드
    public void setMember(Member member) {
        this.member = member;
        member.getOrders().add(this);
    }

    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }
}

참고 자료

이 내용은 다음 자료들을 참고하여 작성되었습니다:

주의사항

  • @GeneratedValue 전략은 데이터베이스에 따라 적절한 것을 선택해야 합니다
  • @OneToMany, @ManyToOne 관계에서는 성능을 위해 지연 로딩(LAZY)을 권장합니다
  • 양방향 관계에서는 연관관계의 주인을 명확히 설정해야 합니다

이러한 JPA 엔티티 매핑을 통해 객체와 테이블을 효과적으로 매핑하고, 객체지향적인 도메인 모델을 구축할 수 있습니다.

반응형

'Java > JPA' 카테고리의 다른 글

JPA & Hibernate Cache  (0) 2022.01.19
JPA Cascade Types  (0) 2021.12.19
반응형

MyBatis Spring 기본 설정 가이드

1. SqlSessionFactory 설정

SqlSessionFactory는 MyBatis의 핵심 컴포넌트로, 데이터베이스 연결과 SQL 실행을 관리합니다.

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mapperLocations" value="classpath:/mapper/**/*.xml" />
    <!-- 선택적 설정 추가 -->
    <property name="configLocation" value="classpath:/mybatis-config.xml" />
    <property name="typeAliasesPackage" value="your.package.domain" />
</bean>

주요 속성 설명:

  • dataSource: 데이터베이스 연결 정보를 담고 있는 빈을 참조합니다
  • mapperLocations: SQL 매퍼 파일들의 위치를 지정합니다
  • configLocation: MyBatis 설정 파일의 위치를 지정합니다
  • typeAliasesPackage: 도메인 객체들의 패키지를 지정하여 별칭을 자동으로 생성합니다

2. SqlSessionTemplate 설정

SqlSessionTemplate은 MyBatis의 SqlSession을 스프링과 연동하기 위한 핵심 클래스입니다. 스레드 안전하며 트랜잭션 관리를 자동으로 처리합니다.

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSession"/>
</bean>

3. 매퍼 스캐닝 설정

매퍼 인터페이스를 자동으로 스캔하고 빈으로 등록하기 위한 설정입니다.

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="your.package.mapper" />
    <!-- 선택적 설정 추가 -->
    <property name="annotationClass" value="org.springframework.stereotype.Repository" />
    <property name="sqlSessionFactoryBeanName" value="sqlSession" />
</bean>

주요 속성 설명:

  • basePackage: 매퍼 인터페이스들이 위치한 패키지를 지정합니다
  • annotationClass: 스캔할 매퍼 인터페이스에 필요한 애노테이션을 지정합니다
  • sqlSessionFactoryBeanName: 사용할 SqlSessionFactory 빈의 이름을 지정합니다

4. Java Configuration 방식

XML 대신 Java Config를 사용할 경우의 예시입니다:

@Configuration
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("classpath:/mapper/**/*.xml"));
        return sessionFactory.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

@Configuration
@MapperScan(basePackages = "your.package.mapper")
public class MapperConfig {
    // 매퍼 스캐닝 설정
}

이러한 설정을 통해 MyBatis는 스프링과 완벽하게 통합되어 동작하며, 데이터베이스 작업을 효율적으로 처리할 수 있습니다.

 

 

참고:

  1. http://www.mybatis.org/spring/ko/mappers.html
반응형

'Java > Spring Framework' 카테고리의 다른 글

스프링 프레임워크 마이그레이션 3.2.9 to 4.3.5  (0) 2016.12.22
Spring 한글설정  (0) 2015.03.19
Spring AOP  (0) 2015.01.19
스프링 스터디 - 의존성 주입 대상  (0) 2015.01.14
스프링 학습 개발 도구 설치  (0) 2015.01.13

+ Recent posts