티스토리 뷰

반응형

 

스프링 프레임워크 사용하다가 궁금한 것이 생겼습니다.

다 같은 어노테이션인데 굳이 왜 나누어 놓았을까? 분명 이유가 있겠죠?

모르는 것은 바로 궁금증을 해결해보아야 되죠!!

 

이번 글에서는 @Bean과 @Component의 차이에 대해 살펴보려고 합니다.


@Bean

외부 라이브러리지만 개발자가 컨트롤이 불가능한 경우 사용한다고 합니다.

예를 들어, Redis를 사용하는 경우가 있습니다.

 

@Configuration
public class RedisConfig {

   private @Value("${spring.redis.host}") String redisHost;
   private @Value("${spring.redis.port}") int redisPort;

   @Bean
   public JedisConnectionFactory connectionFactory() {
      JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
      jedisConnectionFactory.setHostName(redisHost);
      jedisConnectionFactory.setPort(redisPort);
      jedisConnectionFactory.setUsePool(true);
      return jedisConnectionFactory;
   }
   
   @Bean
   public RedisTemplate<String, Object> redisTemplate() {
      RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
      redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setValueSerializer(new StringRedisSerializer());
      redisTemplate.setHashKeySerializer(new StringRedisSerializer());
      redisTemplate.setHashValueSerializer(new StringRedisSerializer());
      redisTemplate.setConnectionFactory(connectionFactory());      
      return redisTemplate;
   }
}

Redis의 경우 application.yml에서 제공받은 변수를 할당받아 적용해야 하는데, 이때 사용합니다.

다른 경우도 한 번 보겠습니다.

 

@Configuration
@EnableAsync
public class AsyncConfig {

   @Bean("customExecutor")
   public TaskExecutor getAsyncExecutor() {
      ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
      executor.setCorePoolSize(10);
      executor.setMaxPoolSize(200);
      executor.setQueueCapacity(30);
      executor.setThreadNamePrefix("Async-");
      executor.setKeepAliveSeconds(5);
      executor.initialize();
      return executor;
   }
}

 이번에는 비동기 서비스에서 사용하는 AsynConfig 파일을 가져와보았습니다. 딱 정해진 값에만 세팅을 해서 return 하고 있네요.

@Configuration
@MapperScan(basePackages = "kr.abbo.app.api", annotationClass = abboDB.class, sqlSessionFactoryRef = "abboSqlSessionFactory")
public class MybatisDatasourceConfig {
   @Bean("abboSqlSessionFactory")
   public SqlSessionFactory abboSqlSessionFactory(@Qualifier("abboDataSource") DataSource dataSource)
         throws Exception {
      SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
      PathMatchingResourcePatternResolver pathResolver = new PathMatchingResourcePatternResolver();
      sessionFactoryBean.setDataSource(dataSource);
      sessionFactoryBean.setTypeAliasesPackage("kr.abbo.app.api.model");
      sessionFactoryBean.setConfigLocation(pathResolver.getResource("classpath:mybatis/MybatisConfiguration.xml"));
      sessionFactoryBean.setMapperLocations(pathResolver.getResources("classpath:**/*Mapper.xml"));
      sessionFactoryBean.setVfs(SpringBootVFS.class);
      return sessionFactoryBean.getObject();
   }
}

마지막으로 지정된 곳은 데이터 소스 부분인데요, 이 부분은 Database에 연결시키기 위해 스키마와 다른 부가 옵션을 설정하는 곳입니다. 위의 설정 파일들을 모두 확인해보니 @Configuration이 설정된 클래스 내에 모두 선언이 되어있고, 각 클래스마다 설정값이 다르게 매겨져 있는 것을 확인할 수 있습니다. 

 

@ComponentScan의 경우는 Spring Boot가 기동 되면서 Bean에 파일을 연결시켜줄 수 있는 어노테이션입니다.


@Component

이번에는 컴포넌트 어노테이션에 대해 정리해볼 텐데요, @Component는 검색해보니 개발자가 컨트롤이 가능한 클래스의 경우 사용한다고 합니다. 예시를 바로 보겠습니다.

@Slf4j
@Component
public class FileUploadUtil {

    // 추후에 application.yml에 value로 불러오도록 수정
    private String ACCESS_KEY = "...";
    private String SECRET_KEY = "......";
    private String BUCKET_NAME = "cdn-s3.abbo.kr";

    private static String DOMAIN = "http://images-cdn.abbo.kr";
    private static String ROOT = "/app/user";
    private static String PATH_FORMAT = "/yyyy/MM/dd";

    public String imageUpload(MultipartFile file) {
        try {
            byte[] fileData = IOUtils.toByteArray(file.getInputStream());
            String sourceFileName = file.getOriginalFilename();
            String fileExt = FilenameUtils.getExtension(sourceFileName).toLowerCase();
            String path = new SimpleDateFormat(PATH_FORMAT).format(new Date());
            String contentType = file.getContentType();
            String fileName = System.currentTimeMillis() + "." + fileExt;

            s3Upload(ROOT + path, fileName, contentType, fileData);

            return DOMAIN + ROOT + path + "/" + fileName;
        } catch (IOException e) {
            log.error(ExceptionUtils.getStackTrace(e));
            return null;
        }
    }
}

파일 업로드를 하는 유틸리티 클래스를 가져와 보았습니다. 오, 이번에는 내부에 로직이 정해져 있네요. 

하나 더 샘플을 봐야 감이 올 것 같습니다.

@Slf4j
@Component
@Profile("prd")
public class MemoryHealth {

   @Autowired ElasticsearchConfig logConfig;

   @PostConstruct
   public void init(){
      ManagementFactory.getMemoryPoolMXBeans().parallelStream().forEach(memoryPoolMXBean -> log.info("memoryPoolMXBean.getName() --- " + memoryPoolMXBean.getName()));
   }

   @Scheduled(initialDelay = 1000, fixedDelay = 60000)
   public void check(){
      Map<String, Object> memInfo = new HashMap<>();

      try {
         memInfo.put("server", InetAddress.getLocalHost().getCanonicalHostName());
      } catch (UnknownHostException e) {
         e.printStackTrace();
      }

      ManagementFactory.getMemoryPoolMXBeans().parallelStream().forEach(memoryPoolMXBean -> {
         switch (memoryPoolMXBean.getName()){
            case "Code Cache":
               memInfo.put("codeCache", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Metaspace":
               memInfo.put("metaSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Compressed Class Space":
               memInfo.put("compressedClassSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Eden Space":
            case "PS Eden Space":
               memInfo.put("psEdenSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Survivor Space":
            case "PS Survivor Space":
               memInfo.put("psSurvivorSpace", memoryPoolMXBean.getUsage().getUsed());
               break;
            case "Tenured Gen":
            case "PS Old Gen":
               memInfo.put("psOldGen", memoryPoolMXBean.getUsage().getUsed());
               break;
         }
         memInfo.put("createdDatetime", System.currentTimeMillis());
      });
   }
}

이번에 작성된 유틸리티는 운영 중인 서버의 메모리 적재량을 확인하기 위해 구현된 소스입니다. 관리하고 있는 포인트가 꽤나 많기에 이러한 소스가 있다고만 짚고 넘어가 보겠습니다.

 

아무래도 @Bean과 @Component의 두 가지 어노테이션을 확인해 보았을 때 꽤나 차이가 있는 것을 확인하실 수 있을 거예요. 


정리

@Bean: 이전에 제공된 라이브러리를 Setter, Builder를 통해 사용자가 Properties를 변경하여 생성한 instance

@Component: 개발자가 생성한 클래스를 Spring Boot에 등록하기 위해 instance를 생성하고 Bean에 등록하는 과정

반응형

'Server' 카테고리의 다른 글

Kotlin의 Any와 Nullable (1)  (0) 2020.06.29
Kotlin의 Control Flow  (0) 2020.06.26
Kotlin의 함수(Function)  (0) 2020.06.26
Kotlin 변수의 Wrapper Type  (0) 2020.06.25
Kotlin 설치법  (0) 2020.06.24
Kotlin이 뭐에요?  (1) 2020.06.22
댓글
공지사항