๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ–ฅ๏ธ Backend

๐ŸŒฑ Spring ํ•ต์‹ฌ ๊ฐœ๋… ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

by hyebin (Helia) 2025. 5. 29.
๋ฐ˜์‘ํ˜•

 


๋ชฉ์ฐจ

     

     

    Spring์€ ๋‹จ์ˆœํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

    ๊ฐ์ฒด ์ง€ํ–ฅ ์„ค๊ณ„, ์˜์กด์„ฑ ์ฃผ์ž…, AOP, ์„ค์ • ์œ ์—ฐ์„ฑ ๋“ฑ ์—ฌ๋Ÿฌ ์ฒ ํ•™๊ณผ ๊ธฐ์ˆ ์ด ๋…น์•„ ์žˆ๋Š” ์ข…ํ•ฉ ๊ฐœ๋ฐœ ํ”Œ๋žซํผ์ด์ฃ .

     

    Spring Boot๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์‹  ๋ถ„์ด๋ผ๋ฉด @Autowired, @Component ๊ฐ™์€ ์–ด๋…ธํ…Œ์ด์…˜์€ ์ต์ˆ™ํ•˜์ง€๋งŒ

    ๋„๋Œ€์ฒด ๋‚ด๋ถ€์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๋Š” ๊ฑธ๊นŒ?” ๋ผ๋Š” ์˜๋ฌธ์ด ํ•œ ๋ฒˆ์ฏค์€ ๋“ค์—ˆ์„ ๊ฒ๋‹ˆ๋‹ค.

     

    ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Spring์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์„ ์‹ค์ œ ์ฝ”๋“œ ์˜ˆ์‹œ์™€ ์‹ค๋ฌด ํ™œ์šฉ ํฌ์ธํŠธ ์ค‘์‹ฌ์œผ๋กœ ์ •๋ฆฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค


    ๐Ÿง  ์ œ์–ด์˜ ์—ญ์ „(IoC)๊ณผ ์˜์กด์„ฑ ์ฃผ์ž…(DI)

    IoC(Inversion of Control) ๋ž€?

    IoC(Inversion of Control)๋Š” ๊ฐ์ฒด์˜ ์ƒ์„ฑ๊ณผ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋ฅผ ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹Œ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋‹ด๋‹นํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งํ•ฉ๋‹ˆ๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  ์ด IoC๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์ด ๋ฐ”๋กœ DI(Dependency Injection), ์ฆ‰ ์˜์กด์„ฑ ์ฃผ์ž…์ž…๋‹ˆ๋‹ค.

     

    ์ „ํ†ต์ ์ธ ๋ฐฉ์‹ (IoC ์ ์šฉ ์ „)

    public class UserService {
        private UserRepository userRepository = new UserRepository(); // ์ง์ ‘ ์ƒ์„ฑ
        
        public User findUser(Long id) {
            return userRepository.findById(id);
        }
    }

     

    Spring IoC ์ ์šฉ ํ›„

    @Service
    public class UserService {
        private final UserRepository userRepository;
        
        // Spring์ด UserRepository๋ฅผ ์ฃผ์ž…ํ•ด์คŒ
        public UserService(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
        
        public User findUser(Long id) {
            return userRepository.findById(id);
        }
    }

     

    ๐Ÿ”ง ํ•ต์‹ฌ ์–ด๋…ธํ…Œ์ด์…˜

    • @Component, @Service, @Repository: ์Šคํ”„๋ง์ด ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๋นˆ ๋“ฑ๋ก
    • @Autowired: ์˜์กด์„ฑ ์ฃผ์ž… ํ‘œ์‹œ (์ƒ์„ฑ์ž/ํ•„๋“œ/์„ธํ„ฐ ๋ฐฉ์‹ ์ง€์›)

     

    ๐Ÿ’ก ํ™œ์šฉ ํฌ์ธํŠธ

    • ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ํ–ฅ์ƒ
    • ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ๊ฐ์ฒด(๋นˆ)์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๊ด€๋ฆฌ

    DI์˜ 3๊ฐ€์ง€ ๋ฐฉ์‹

    1. ์ƒ์„ฑ์ž ์ฃผ์ž… (๊ถŒ์žฅ)

    @Service
    public class UserService {
        private final UserRepository userRepository;
        private final EmailService emailService;
        
        // ์ƒ์„ฑ์ž๊ฐ€ ํ•˜๋‚˜๋ฉด @Autowired ์ƒ๋žต ๊ฐ€๋Šฅ
        public UserService(UserRepository userRepository, EmailService emailService) {
            this.userRepository = userRepository;
            this.emailService = emailService;
        }
    }

     

    2. ํ•„๋“œ ์ฃผ์ž… (ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ค์›€, ๋น„์ถ”์ฒœ)

    @Service
    public class UserService {
        @Autowired
        private UserRepository userRepository; // ๊ถŒ์žฅํ•˜์ง€ ์•Š์Œ
    }

     

    3. ์„ธํ„ฐ ์ฃผ์ž… (์„ ํƒ์  ์˜์กด์„ฑ์— ์‚ฌ์šฉ)

    @Service
    public class UserService {
        private UserRepository userRepository;
        
        @Autowired
        public void setUserRepository(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    }
    ์ƒ์„ฑ์ž ์ฃผ์ž…์€ final ํ‚ค์›Œ๋“œ๋ฅผ ํ™œ์šฉํ•ด ๋ถˆ๋ณ€์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๊ณ , Mock ์ฃผ์ž… ํ…Œ์ŠคํŠธ์—๋„ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

    ๐Ÿงฌ Bean ์ƒ๋ช…์ฃผ๊ธฐ์™€ ์Šค์ฝ”ํ”„ ์ดํ•ดํ•˜๊ธฐ

    ์Šคํ”„๋ง์ด ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด๋ฅผ Bean์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

    ์ด Bean์€ ์ƒ์„ฑ๋ถ€ํ„ฐ ์†Œ๋ฉธ๊นŒ์ง€ IoC ์ปจํ…Œ์ด๋„ˆ์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋ฉฐ, ์Šค์ฝ”ํ”„(scope)์— ๋”ฐ๋ผ ๊ทธ ์ˆ˜๋ช…๋„ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

     

    ์ƒ๋ช…์ฃผ๊ธฐ ํ›‘์–ด๋ณด๊ธฐ

    1. Bean ๊ฐ์ฒด ์ƒ์„ฑ
    2. ์˜์กด์„ฑ ์ฃผ์ž… (DI)
    3. ์ดˆ๊ธฐํ™” (@PostConstruct)
    4. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์ค‘ ์‚ฌ์šฉ
    5. ์ข…๋ฃŒ ์‹œ ์†Œ๋ฉธ (@PreDestroy)
    @Component
    public class MyBean {
        @PostConstruct
        public void init() {
            System.out.println("์ดˆ๊ธฐํ™” ์™„๋ฃŒ");
        }
    
        @PreDestroy
        public void destroy() {
            System.out.println("์†Œ๋ฉธ ์ฒ˜๋ฆฌ");
        }
    }

     

    Bean ์Šค์ฝ”ํ”„ ์ข…๋ฅ˜

    ์Šค์ฝ”ํ”„ ์„ค๋ช…
    singleton ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ ์ƒ์„ฑ (๊ธฐ๋ณธ๊ฐ’)
    prototype ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    request HTTP ์š”์ฒญ๋งˆ๋‹ค ํ•˜๋‚˜ (์›น ํ™˜๊ฒฝ)
    session ์‚ฌ์šฉ์ž ์„ธ์…˜๋งˆ๋‹ค ํ•˜๋‚˜ (์›น ํ™˜๊ฒฝ)
    ๋Œ€๋ถ€๋ถ„์˜ ์„œ๋น„์Šค ๋กœ์ง์€ singleton ์Šค์ฝ”ํ”„๋กœ ์ž‘์„ฑ๋˜๋ฉฐ, ํ•„์š”์— ๋”ฐ๋ผ prototype ๋“ฑ์„ ์กฐํ•ฉํ•ฉ๋‹ˆ๋‹ค.

    โœจ AOP๋กœ ์ค‘๋ณต ์ฝ”๋“œ ์ค„์ด๊ธฐ

    AOP(Aspect-Oriented Programming, ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ)๋Š” ๊ณตํ†ต ๊ด€์‹ฌ์‚ฌ(Cross-cutting Concern)๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ

    ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋” ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•์Šต๋‹ˆ๋‹ค.

     

    AOP ์—†์ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ 

    @Service
    public class UserService {
        
        public User createUser(String name, String email) {
            // ๋กœ๊น… ์ฝ”๋“œ - ์ค‘๋ณต!
            log.info("์‚ฌ์šฉ์ž ์ƒ์„ฑ ์‹œ์ž‘: name={}, email={}", name, email);
            
            // ๊ถŒํ•œ ์ฒดํฌ ์ฝ”๋“œ - ์ค‘๋ณต!
            if (!SecurityUtils.hasPermission("USER_CREATE")) {
                throw new AccessDeniedException("๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค");
            }
            
            // ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ • - ์ค‘๋ณต!
            long startTime = System.currentTimeMillis();
            
            try {
                // ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
                User user = new User(name, email);
                userRepository.save(user);
                
                log.info("์‚ฌ์šฉ์ž ์ƒ์„ฑ ์™„๋ฃŒ: userId={}", user.getId());
                return user;
                
            } finally {
                long endTime = System.currentTimeMillis();
                log.info("์‹คํ–‰ ์‹œ๊ฐ„: {}ms", endTime - startTime);
            }
        }
    }

     

    AOP๋ฅผ ์ ์šฉํ•œ ๊น”๋”ํ•œ ์ฝ”๋“œ

    @Service
    public class UserService {
        
        @LogExecutionTime  // ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •
        @RequiredPermission("USER_CREATE")  // ๊ถŒํ•œ ์ฒดํฌ
        @Transactional  // ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ
        public User createUser(String name, String email) {
            // ์ˆœ์ˆ˜ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๋งŒ!
            User user = new User(name, email);
            return userRepository.save(user);
        }
    }
    ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ , ๋ถ€๊ฐ€ ๋กœ์ง์€ Aspect๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋Œ€ํญ ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

     

    ์‹ค์ œ AOP ๊ตฌํ˜„ ์˜ˆ์‹œ

    1. ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ • Aspect

    @Aspect
    @Component
    @Slf4j
    public class ExecutionTimeAspect {
        
        @Around("@annotation(LogExecutionTime)")
        public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
            long startTime = System.currentTimeMillis();
            
            try {
                Object result = joinPoint.proceed(); // ์‹ค์ œ ๋ฉ”์„œ๋“œ ์‹คํ–‰
                return result;
            } finally {
                long endTime = System.currentTimeMillis();
                String methodName = joinPoint.getSignature().getName();
                log.info("๋ฉ”์„œ๋“œ [{}] ์‹คํ–‰ ์‹œ๊ฐ„: {}ms", methodName, endTime - startTime);
            }
        }
    }
    
    // ์ปค์Šคํ…€ ์–ด๋…ธํ…Œ์ด์…˜
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LogExecutionTime {
    }

     

    2. ์˜ˆ์™ธ ์ฒ˜๋ฆฌ Aspect

    @Aspect
    @Component
    @Slf4j
    public class ExceptionHandlingAspect {
        
        @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", 
                       throwing = "exception")
        public void handleException(JoinPoint joinPoint, Exception exception) {
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
            
            log.error("๋ฉ”์„œ๋“œ [{}] ์‹คํ–‰ ์ค‘ ์˜ˆ์™ธ ๋ฐœ์ƒ. ํŒŒ๋ผ๋ฏธํ„ฐ: {}", 
                      methodName, Arrays.toString(args), exception);
            
            // ์ถ”๊ฐ€์ ์ธ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋กœ์ง (์•Œ๋ฆผ, ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘ ๋“ฑ)
            notificationService.sendErrorAlert(methodName, exception);
        }
    }

    โš™๏ธ Spring Boot ์ž๋™ ์„ค์ •์˜ ๋น„๋ฐ€

    Spring Boot์˜ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ํŠน์ง• ์ค‘ ํ•˜๋‚˜๋Š” ์ž๋™ ์„ค์ •(Auto Configuration)์ž…๋‹ˆ๋‹ค.

    ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ณ„๋„๋กœ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„, ํด๋ž˜์ŠคํŒจ์Šค์— ์กด์žฌํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์„ค์ • ํŒŒ์ผ์„ ๋ถ„์„ํ•ด์„œ ํ•„์š”ํ•œ ์„ค์ •์„ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑํ•ด ์ค๋‹ˆ๋‹ค.

     

     @SpringBootApplication์ด๋ž€?

    @SpringBootApplication

    ์ด ํ•œ ์ค„ ์–ด๋…ธํ…Œ์ด์…˜์€ ์‚ฌ์‹ค ์„ธ ๊ฐ€์ง€ ํ•ต์‹ฌ ์–ด๋…ธํ…Œ์ด์…˜์„ ์กฐํ•ฉํ•œ ๋ฉ”ํƒ€ ์–ด๋…ธํ…Œ์ด์…˜(Meta-Annotation) ์ž…๋‹ˆ๋‹ค.

     

    • @Configuration: ๋นˆ ๋“ฑ๋ก ํด๋ž˜์Šค
    • @ComponentScan: ์ปดํฌ๋„ŒํŠธ ์Šค์บ”
    • @EnableAutoConfiguration: ์˜์กด์„ฑ ๊ธฐ๋ฐ˜ ์ž๋™ ์„ค์ •

    ์ฆ‰, @SpringBootApplication๋งŒ ๋ถ™์ด๋ฉด ์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ์ž‘ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๊ฑฐ์˜ ๋ชจ๋“  ์„ค์ •์ด ์ž๋™์œผ๋กœ ์ค€๋น„๋ฉ๋‹ˆ๋‹ค.

     

     ๋™์ž‘ ๋ฐฉ์‹

    Spring Boot๋Š” META-INF/spring.factories ๋˜๋Š” ์ตœ์‹  ๋ฒ„์ „์—์„œ๋Š”

    META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ํŒŒ์ผ์„ ์ฐธ๊ณ ํ•ด์„œ ์ž๋™ ์„ค์ • ํ›„๋ณด ํด๋ž˜์Šค๋ฅผ ๋กœ๋”ฉํ•ฉ๋‹ˆ๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  ์ด ์„ค์ •๋“ค์€ ์กฐ๊ฑด๋ถ€ ์–ด๋…ธํ…Œ์ด์…˜(@Conditional) ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

    @Bean
    @ConditionalOnClass(HikariDataSource.class)
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource dataSource() {
        return new HikariDataSource();
    }
    ์„ค์ •์„ ๋ชฐ๋ผ๋„ ์“ธ ์ˆ˜ ์žˆ์ง€๋งŒ, ์›๋ฆฌ๋ฅผ ์•Œ๋ฉด ๋””๋ฒ„๊น…๊ณผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ํ›จ์”ฌ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

    ๐Ÿ”ง ์™ธ๋ถ€ ์„ค์ •๊ณผ ํ”„๋กœํŒŒ์ผ๋กœ ํ™˜๊ฒฝ ๊ด€๋ฆฌํ•˜๊ธฐ

    ์‹ค๋ฌด์—์„œ๋Š” ๋กœ์ปฌ, ๊ฐœ๋ฐœ, ์šด์˜ ํ™˜๊ฒฝ๋งˆ๋‹ค ์„ค์ •์ด ๋‹ฌ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    Spring Boot๋Š” ์ด๋ฅผ ์œ„ํ•ด ํ”„๋กœํŒŒ์ผ(Profile) ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    ํ”„๋กœํŒŒ์ผ๋ณ„ ์„ค์ • ํŒŒ์ผ ๊ตฌ์„ฑ

    src/main/resources/
    โ”œโ”€โ”€ application.yml              # ๊ณตํ†ต ์„ค์ •
    โ”œโ”€โ”€ application-dev.yml          # ๊ฐœ๋ฐœ ํ™˜๊ฒฝ
    โ”œโ”€โ”€ application-test.yml         # ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ
    โ””โ”€โ”€ application-prod.yml         # ์šด์˜ ํ™˜๊ฒฝ
    # application.yml
    spring:
      profiles:
        active: dev
    
    # application-dev.yml
    spring:
      datasource:
        url: jdbc:h2:mem:testdb
    
    # application-prod.yml
    spring:
      datasource:
        url: jdbc:mysql://prod-db:3306/app

     

    @ConfigurationProperties ์‚ฌ์šฉ

    @ConfigurationProperties๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์™ธ๋ถ€ ์„ค์ •์„ ๊ฐ์ฒด๋กœ ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    @ConfigurationProperties(prefix = "app")
    @Component
    @Data
    public class AppProperties {
        
        private String name;
        private String version;
        private Security security = new Security();
        private Mail mail = new Mail();
        
        @Data
        public static class Security {
            private String jwtSecret;
            private long jwtExpirationMs = 86400000; // 24์‹œ๊ฐ„
            private List<String> allowedOrigins = new ArrayList<>();
        }
        
        @Data
        public static class Mail {
            private String host;
            private int port = 587;
            private String username;
            private String password;
            private boolean enabled = true;
        }
    }

    ๐ŸŽจ ์‹ค๋ฌด์—์„œ ์ž์ฃผ ์“ฐ์ด๋Š” ์Šคํ”„๋ง ๊ธฐ๋Šฅ๋“ค

    ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜

    // ์ด๋ฒคํŠธ ์ •์˜
    public class UserRegisteredEvent {
        private final String userId;
        private final String email;
        private final LocalDateTime registeredAt;
        
        // ์ƒ์„ฑ์ž, getter
    }
    
    // ์ด๋ฒคํŠธ ๋ฐœํ–‰
    @Service
    @RequiredArgsConstructor
    public class UserService {
        
        private final ApplicationEventPublisher eventPublisher;
        
        @Transactional
        public User registerUser(String name, String email) {
            User user = userRepository.save(new User(name, email));
            
            // ์ด๋ฒคํŠธ ๋ฐœํ–‰
            eventPublisher.publishEvent(
                new UserRegisteredEvent(user.getId(), user.getEmail(), LocalDateTime.now())
            );
            
            return user;
        }
    }
    
    // ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ
    @Component
    @Slf4j
    public class UserEventListener {
        
        @EventListener
        @Async
        public void handleUserRegistered(UserRegisteredEvent event) {
            log.info("์ƒˆ ์‚ฌ์šฉ์ž ๋“ฑ๋ก๋จ: {}", event.getUserId());
            
            // ํ™˜์˜ ์ด๋ฉ”์ผ ๋ฐœ์†ก
            emailService.sendWelcomeEmail(event.getEmail());
            
            // ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ ๋“ฑ
            statisticsService.incrementUserCount();
        }
    }

     

    ์Šค์ผ€์ค„๋ง

    @Component
    @EnableScheduling
    @Slf4j
    public class ScheduledTasks {
        
        @Scheduled(fixedRate = 300000) // 5๋ถ„๋งˆ๋‹ค
        public void healthCheck() {
            log.info("์‹œ์Šคํ…œ ์ƒํƒœ ์ฒดํฌ ์‹คํ–‰");
            systemHealthService.performHealthCheck();
        }
        
        @Scheduled(cron = "0 0 2 * * ?") // ๋งค์ผ ์ƒˆ๋ฒฝ 2์‹œ
        public void dailyReportGeneration() {
            log.info("์ผ์ผ ๋ฆฌํฌํŠธ ์ƒ์„ฑ ์‹œ์ž‘");
            reportService.generateDailyReport();
        }
    }

    ๐Ÿงก ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

    ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Spring์˜ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์„ ์‹ค๋ฌด ๊ด€์ ์—์„œ ์‚ดํŽด๋ดค์Šต๋‹ˆ๋‹ค.

     

    ๐Ÿ“Œ ํ•ต์‹ฌ ์š”์•ฝ

    • IoC/DI: ๊ฐ์ฒด ๊ด€๋ฆฌ๋ฅผ ์Šคํ”„๋ง์—๊ฒŒ ๋งก๊ธฐ๊ณ , ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถฐ ํ…Œ์ŠคํŠธ์™€ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์šด ๊ตฌ์กฐ ์„ค๊ณ„
    • Bean ์ƒ๋ช…์ฃผ๊ธฐ/์Šค์ฝ”ํ”„: ๊ฐ์ฒด ์ˆ˜๋ช…๊ณผ ์‚ฌ์šฉ ๋ฒ”์œ„๋ฅผ ๋ช…ํ™•ํžˆ ์ดํ•ด
    • AOP: ์ค‘๋ณต ๋กœ์ง ์ œ๊ฑฐ, ๋น„์ฆˆ๋‹ˆ์Šค ์ฝ”๋“œ์˜ ์ˆœ์ˆ˜์„ฑ ์œ ์ง€
    • ํ™˜๊ฒฝ ์„ค์ •/ํ”„๋กœํŒŒ์ผ: ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ๋ถ„๋ฆฌ๋กœ ์‹ค๋ฌด ์œ ์—ฐ์„ฑ ํ™•๋ณด

    ์ด ๊ฐœ๋…๋“ค์„ ์ •ํ™•ํžˆ ์ดํ•ดํ•˜๊ณ  ๋‚˜๋ฉด, Spring Boot๋ฅผ ๋” ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ํŠนํžˆ ์‹ค๋ฌด์—์„œ๋Š” AOP๋ฅผ ํ†ตํ•œ ๋กœ๊น…, ํ”„๋กœํŒŒ์ผ์„ ํ†ตํ•œ ํ™˜๊ฒฝ ๋ถ„๋ฆฌ, ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜ ๋“ฑ์ด ๋งค์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋ผ์š”.

     

    ๋‹ค์Œ ๊ธ€์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™๊ณผ JPA์— ๋Œ€ํ•ด ๋‹ค๋ค„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    ๊ถ๊ธˆํ•œ ์ ์ด๋‚˜ ๋” ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ์€ ๊ฐœ๋…์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€๋กœ ๋‚จ๊ฒจ์ฃผ์„ธ์š”! ๐Ÿš€

    ๋ฐ˜์‘ํ˜•