Avatar
๐Ÿ˜‰

Organizations

  • Java์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ SELECT ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด Lock๊ณผ Unlock์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ์Šคํ…œ๊ณผ ์ฟผ๋ฆฌ์˜ ์„ค์ •์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ SELECT ์ž์ฒด๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ž ๊ธˆ์„ ์œ ๋ฐœํ•˜์ง€ ์•Š์ง€๋งŒ, ํŠธ๋žœ์žญ์…˜์ด๋‚˜ ์ฟผ๋ฆฌ ํžŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž ๊ธˆ ๋™์ž‘์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์— ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.


    1. ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•œ Lock ์ œ์–ด

    ํŠธ๋žœ์žญ์…˜์„ ํ†ตํ•ด SELECT ๋ฌธ์ด ์‹คํ–‰ ์ค‘์ธ ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์ด ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜์ง€ ๋ชปํ•˜๋„๋ก ์ž ๊ธˆ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ์˜ˆ์ œ ์ฝ”๋“œ:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    public class DatabaseLockExample {
        public static void main(String[] args) {
            String url = "jdbc:mysql://localhost:3306/your_database";
            String user = "your_username";
            String password = "your_password";
    
            try (Connection connection = DriverManager.getConnection(url, user, password)) {
                // Auto-commit ๋น„ํ™œ์„ฑํ™”
                connection.setAutoCommit(false);
    
                // SELECT ... FOR UPDATE (ํ–‰ ์ž ๊ธˆ ์„ค์ •)
                String sql = "SELECT * FROM your_table WHERE id = ? FOR UPDATE";
                try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
                    preparedStatement.setInt(1, 1); // ID ๊ฐ’ ๋ฐ”์ธ๋”ฉ
                    ResultSet resultSet = preparedStatement.executeQuery();
    
                    while (resultSet.next()) {
                        System.out.println("Data: " + resultSet.getString("your_column"));
                    }
    
                    // ์ž‘์—… ํ›„ ์ปค๋ฐ‹
                    connection.commit();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    ์„ค๋ช…:

    • FOR UPDATE๋Š” ํ•ด๋‹น ํ–‰์„ ์ž ๊ทธ๊ณ , ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ์ˆ˜์ •ํ•˜์ง€ ๋ชปํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
    • ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•˜๊ฑฐ๋‚˜ ๋กค๋ฐฑํ•˜๋ฉด ์ž ๊ธˆ์ด ํ•ด์ œ๋ฉ๋‹ˆ๋‹ค.

    2. ์ฟผ๋ฆฌ ํžŒํŠธ๋ฅผ ์‚ฌ์šฉํ•œ Lock ์ œ์–ด

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋”ฐ๋ผ SELECT ์ฟผ๋ฆฌ์— ํžŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ž ๊ธˆ ๋™์ž‘์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, MySQL์—์„œ LOCK IN SHARE MODE ๋˜๋Š” FOR UPDATE๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Created Wed, 26 Mar 2025 11:25:45 +0900
  • Spring Boot JPA์—์„œ @Transactional ์‚ฌ์šฉ๋ฒ•

    Spring Boot JPA์—์„œ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์€ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„ ๋‚ด์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ์‹คํ–‰ํ•˜๊ณ , ์‹คํŒจ ์‹œ ๋กค๋ฐฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ค‘์š”ํ•œ ์š”์†Œ์ž…๋‹ˆ๋‹ค.


    1. @Transactional ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

    Spring์˜ @Transactional์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ JPA ์ž‘์—…(์กฐํšŒ, ์ €์žฅ, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ)์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ํŠธ๋žœ์žญ์…˜์„ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    public class MemberService {
        private final MemberRepository memberRepository;
    
        public MemberService(MemberRepository memberRepository) {
            this.memberRepository = memberRepository;
        }
    
        @Transactional
        public void updateMember(Long id, String name) {
            Member member = memberRepository.findById(id)
                    .orElseThrow(() -> new IllegalArgumentException("Member not found"));
    
            member.setName(name);  // JPA ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋จ
        }
    }
    
    • @Transactional์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ค‘ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋กค๋ฐฑ๋˜๋ฉฐ, ์„ฑ๊ณตํ•˜๋ฉด ์ž๋™์œผ๋กœ ์ปค๋ฐ‹๋ฉ๋‹ˆ๋‹ค.
    • member.setName(name);์„ ํ˜ธ์ถœํ•˜๋ฉด JPA์˜ ๋ณ€๊ฒฝ ๊ฐ์ง€(dirty checking) ๋•๋ถ„์— save()๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.

    2. ์ฝ๊ธฐ ์ „์šฉ ํŠธ๋žœ์žญ์…˜ (@Transactional(readOnly = true))

    readOnly = true๋ฅผ ์„ค์ •ํ•˜๋ฉด ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    Created Wed, 26 Mar 2025 11:21:45 +0900
  • Spring Boot JPA์—์„œ @Transactional ์˜ต์…˜ ์ •๋ฆฌ

    Spring Boot JPA์—์„œ @Transactional์€ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋งค์šฐ ์ค‘์š”ํ•œ ์–ด๋…ธํ…Œ์ด์…˜์ด์—์š”. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ผ๊ด€์„ฑ๊ณผ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ์ž‘์—…์„ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์–ด ์‹คํ–‰ํ•  ๋•Œ ์‚ฌ์šฉํ•˜์ฃ . ์•„๋ž˜์— ์‚ฌ์šฉ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ์š”.


    ๐Ÿ“Œ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

    import org.springframework.transaction.annotation.Transactional;
    
    @Service
    public class MemberService {
    
        @Transactional
        public void joinMember(Member member) {
            memberRepository.save(member);
            // ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์ „์ฒด ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
        }
    }
    
    • @Transactional์„ ๋ฉ”์„œ๋“œ์— ๋ถ™์ด๋ฉด ํ•ด๋‹น ๋ฉ”์„œ๋“œ ์•ˆ์˜ ์ž‘์—…์ด ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌ๋ผ์š”.
    • ๋Ÿฐํƒ€์ž„ ์ค‘ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋กค๋ฐฑ๋ผ์š”. (๊ธฐ๋ณธ์ ์œผ๋กœ unchecked exception์ผ ๋•Œ๋งŒ ๋กค๋ฐฑ)

    ๐Ÿ“Œ ํด๋ž˜์Šค ๋ ˆ๋ฒจ์— ์‚ฌ์šฉ

    @Transactional
    @Service
    public class MemberService {
        public void method1() {
            // ํŠธ๋žœ์žญ์…˜ ์ ์šฉ๋จ
        }
    
        public void method2() {
            // ํŠธ๋žœ์žญ์…˜ ์ ์šฉ๋จ
        }
    }
    
    • ํด๋ž˜์Šค ์ „์ฒด์— ํŠธ๋žœ์žญ์…˜์„ ๊ฑธ ์ˆ˜ ์žˆ์–ด์š”. ๋‹จ, ๋ฉ”์„œ๋“œ์— ๊ฐœ๋ณ„์ ์œผ๋กœ ์„ค์ •๋œ ๊ฒŒ ์šฐ์„  ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

    ๐Ÿ“Œ rollbackFor ์˜ต์…˜

    ๊ธฐ๋ณธ์ ์œผ๋กœ RuntimeException๊ณผ ๊ทธ ํ•˜์œ„ ํด๋ž˜์Šค๋งŒ ๋กค๋ฐฑ ๋Œ€์ƒ์œผ๋กœ ๊ฐ„์ฃผ๋ผ์š”. ๋งŒ์•ฝ ์ฒดํฌ ์˜ˆ์™ธ(Exception)๋„ ๋กค๋ฐฑํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด:

    Created Wed, 26 Mar 2025 11:15:45 +0900
  • Java์—์„œ Redis๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ CRUD Repository์™€ RedisTemplate์€ ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ˆ˜์ค€์˜ ์ถ”์ƒํ™”๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ฃผ์š” ์ฐจ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:


    โœ… 1. CRUD Repository (Spring Data Redis)

    RedisRepository, CrudRepository, PagingAndSortingRepository ๋“ฑ๊ณผ ๊ฐ™์ด Spring Data๊ฐ€ ์ œ๊ณตํ•˜๋Š” Repository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

    ๐Ÿ”น ์žฅ์ 

    • ๋†’์€ ์ถ”์ƒํ™” ์ˆ˜์ค€: ๋‹จ์ˆœํ•œ CRUD ์ž‘์—…์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ •์˜ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด, ๊ตฌํ˜„ ์—†์ด ์ž๋™์œผ๋กœ ๋™์ž‘.
    • ๊ฐœ๋ฐœ ์†๋„ ๋น ๋ฆ„: ๋ฐ˜๋ณต์ ์ธ ๋กœ์ง ์ค„์ผ ์ˆ˜ ์žˆ์Œ (findById, save, delete ๋“ฑ ๊ธฐ๋ณธ ์ œ๊ณต).
    • Spring Data์˜ ๊ธฐ๋Šฅ์„ ํ™œ์šฉ ๊ฐ€๋Šฅ (์˜ˆ: ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ, ํŽ˜์ด์ง• ๋“ฑ).

    ๐Ÿ”น ๋‹จ์ 

    • ์„ธ๋ฐ€ํ•œ ์ œ์–ด ์–ด๋ ค์›€: ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋‚˜ ์ปค์Šคํ…€ ๋กœ์ง์€ ๊ตฌํ˜„ํ•˜๊ธฐ ์–ด๋ ค์›€.
    • ์ง๋ ฌํ™” ๋ฐฉ์‹ ๊ณ ์ •: ๊ธฐ๋ณธ์ ์œผ๋กœ JDK ์ง๋ ฌํ™”๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•˜๋ ค๋ฉด ์„ค์ • ํ•„์š”.

    ๐Ÿ”น ์˜ˆ์‹œ

    public interface UserRedisRepository extends CrudRepository<User, String> {
        Optional<User> findByUsername(String username);
    }
    

    โœ… 2. RedisTemplate

    Spring์—์„œ ์ œ๊ณตํ•˜๋Š” ์ €์ˆ˜์ค€ API๋กœ, ํ‚ค-๊ฐ’ ์ €์žฅ์†Œ๋กœ์„œ Redis๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Œ.

    Created Wed, 26 Mar 2025 11:11:03 +0900
  • feign client ์‚ฌ์šฉํŒ 1 - ์ƒ๋Œ€ ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ๋Œ€ ์ผ๋•Œ ๋žœ๋ค ํ˜ธ์ถœ

    load balancing ๋˜๊ณ  ์žˆ๋Š” ์ƒ๋Œ€์ธก์— ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉด์„œ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•

    • build.gradle
    ...
    dependencies {
        implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
    }
    ...
    
    • application.yml
    # ์ƒ๋Œ€ ์ธก์—์„œ 3๊ฐœ์˜ ์„œ๋ฒ„ LB๋˜๊ณ  ์žˆ์„ ๋•Œ 
    other-party-url:
      - 123.456.789.001
      - 123.456.789.002
      - 123.456.789.003
    
    • ServerProperties.java
    @Setter
    @Getter
    @Configuration
    @ConfigurationProperties(prefix = "other-party-url")
    public class OtherServerProperties {
    
        private List<String> serverUris;
    }
    
    • FeignConfig.java
    @RequiredArgsConstructor
    public class OtherServerFeignConfig {
    
        private final OtherServerProperties otherServerProperties;
    
        private final AtomicInteger counter = new AtomicInteger(0);
    
        @Bean
        public RequestInterceptor randomServerInterceptor() {
            return requestTemplate -> {
                List<String> serverUris = otherServerProperties.getServerUris();
    
                int index = counter.getAndIncrement() % serverUris.size();
                String selectedUrl = serverUris.get(index);
    
                requestTemplate.target(selectedUrl);
            };
        }
    }
    
    • FeignClient interface
    @FeignClient(name = "your-server", configuration = OtherServerFeignConfig.class)
    public interface OtherServerFeignClientV1 {
        
        ...
    }
    
    Created Wed, 26 Mar 2025 08:37:45 +0900

  • ๐Ÿš€ DispatcherServlet ์ดˆ๊ธฐํ™” ๊ณผ์ •

    1. ServletContainer๊ฐ€ DispatcherServlet์„ ๋กœ๋”ฉ

    Spring Boot ์•ฑ์„ ์‹คํ–‰ํ•˜๋ฉด, ๋‚ด์žฅ ํ†ฐ์บฃ์ด ๋œจ๊ณ  ์•„๋ž˜ ๊ณผ์ •์ด ์‹œ์ž‘๋ผ์š”:

    • Spring Boot๊ฐ€ ์ž๋™์œผ๋กœ DispatcherServlet์„ ์„œ๋ธ”๋ฆฟ์œผ๋กœ ๋“ฑ๋กํ•จ (/ ๋งคํ•‘)
    • DispatcherServlet์€ ์‚ฌ์‹ค HttpServlet์„ ์ƒ์†๋ฐ›์€ ํด๋ž˜์Šค
    • ์ด๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ โ†’ init() โ†’ ๊ทธ ์•ˆ์—์„œ initServletBean() ์‹คํ–‰๋จ

    2. WebApplicationContext ์ƒ์„ฑ ๋ฐ ์ดˆ๊ธฐํ™”

    • DispatcherServlet์€ ์ž์‹ ์˜ Spring WebApplicationContext๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
    • ์ด WebApplicationContext๋Š” @Controller, @RestController, @Service, @Repository ๋“ฑ์ด ๋“ฑ๋ก๋œ ๊ณต๊ฐ„
    GenericWebApplicationContext
          โ†‘
    AnnotationConfigWebApplicationContext
          โ†‘
    WebApplicationContext โ† DispatcherServlet์ด ์‚ฌ์šฉ
    

    DispatcherServlet์€ ์ด context๋ฅผ ํ†ตํ•ด ๋นˆ์„ ์ฐพ๊ณ , ๋ผ์šฐํŒ…์„ ์ˆ˜ํ–‰ํ•จ

    Created Wed, 26 Mar 2025 08:20:45 +0900