Avatar
๐Ÿ˜‰

Organizations

  • DB์˜ ํ…Œ์ด๋ธ”์˜ row๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์งˆ๋–„ (์˜ˆ ํ•˜๋ฃจ์— 10๋งŒ๊ฑด์”ฉ ๋ˆ„์ ), ๋Œ€์ฒ˜ ๋ฐฉ๋ฒ•

    • โ€œํ•˜๋ฃจ 10๋งŒ ๊ฑด์”ฉ ๊ณ„์† ๋ˆ„์ โ€์ด๋ฉด 1๋…„์— 3,650๋งŒ ๊ฑด์ž…๋‹ˆ๋‹ค. ์ดˆ๋ฐ˜์—” ๋ฒ„ํ‹ฐ์ง€๋งŒ, ๋ช‡ ๋‹ฌ๋งŒ ์ง€๋‚˜๋„ ์ฟผ๋ฆฌยท๋ฐฑ์—…ยทVACUUM/OPTIMIZE ๋น„์šฉ์ด ๊ธ‰์ฆํ•˜์ฃ . ์•„๋ž˜ ์ˆœ์„œ๋Œ€๋กœ ์ ์šฉํ•ด๋ณด์„ธ์š”.

    1) ๋จผ์ € ๊ฒฐ์ •ํ•  ๊ฒƒ (์šด์˜ ์ •์ฑ…)

    • ๋ณด์กด ๊ธฐ๊ฐ„: ์˜ˆ) โ€œ6๊ฐœ์›”์€ ์˜จ๋ผ์ธ(ํ•ซ), 2๋…„์€ ์•„์นด์ด๋ธŒ(์ฝœ๋“œ), ๊ทธ ์™ธ ์‚ญ์ œโ€
    • ์ฃผ์š” ์กฐํšŒ ํŒจํ„ด: ์ตœ๊ทผ N์ผ๋งŒ ์กฐํšŒ? ํŠน์ • ์‚ฌ์šฉ์ž/ํ‚ค ๊ธฐ์ค€ ์กฐํšŒ? ์ง‘๊ณ„ ์œ„์ฃผ?
    • SLA/์„ฑ๋Šฅ: p95 ์‘๋‹ต์‹œ๊ฐ„, ํ—ˆ์šฉ ๊ฐ€๋Šฅํ•œ ์“ฐ๊ธฐ ์ง€์—ฐ

    2) ์ฆ‰์‹œ ํšจ๊ณผ (์Šคํ‚ค๋งˆ/์ธ๋ฑ์Šค/์“ฐ๊ธฐ)

    • ์ธ๋ฑ์Šค ์Šฌ๋ฆผํ™”: ์‹ค์ œ ์กฐ๊ฑด/์ •๋ ฌ์— ์“ฐ๋Š” ์ปฌ๋Ÿผ๋งŒ ์ธ๋ฑ์Šค. ๋ถˆํ•„์š”ํ•œ ๋ณตํ•ฉ ์ธ๋ฑ์Šค ์ œ๊ฑฐ.

    Created Tue, 14 Oct 2025 11:25:18 +0900
  • HTTP API ํ˜ธ์ถœ ์‹œ access token์„ ํ—ค๋”์™€ ๋ฐ”๋””์— ๋ณด๋‚ด๋Š” ๊ฒƒ ์‚ฌ์ด์—๋Š” ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ๋ณด์•ˆ์ƒ ์ฐจ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค:

    ์ฃผ์š” ๋ณด์•ˆ ์ฐจ์ด์ 

    1. ๋กœ๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง

    • ํ—ค๋”: ๋Œ€๋ถ€๋ถ„์˜ ์›น ์„œ๋ฒ„์™€ ํ”„๋ก์‹œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Authorization ํ—ค๋”๋ฅผ ๋กœ๊ทธ์—์„œ ๋งˆ์Šคํ‚นํ•˜๊ฑฐ๋‚˜ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค
    • ๋ฐ”๋””: ์š”์ฒญ ๋ฐ”๋””๋Š” ๋””๋ฒ„๊น… ๋ชฉ์ ์œผ๋กœ ๋กœ๊ทธ์— ๊ธฐ๋ก๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„ ํ† ํฐ์ด ํ‰๋ฌธ์œผ๋กœ ๋…ธ์ถœ๋  ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค

    2. ์บ์‹ฑ ๋™์ž‘

    • ํ—ค๋”: HTTP ์บ์‹œ๋Š” Authorization ํ—ค๋”๊ฐ€ ์žˆ๋Š” ์š”์ฒญ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์บ์‹œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
    • ๋ฐ”๋””: ๋ฐ”๋””์— ํ† ํฐ์ด ์žˆ์–ด๋„ ์บ์‹œ ์ •์ฑ…์— ๋”ฐ๋ผ ์‘๋‹ต์ด ์บ์‹œ๋  ์ˆ˜ ์žˆ์–ด, ์˜๋„์น˜ ์•Š์€ ํ† ํฐ ๋…ธ์ถœ ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค

    3. ํ”„๋ก์‹œ ๋ฐ ๋ฏธ๋“ค์›จ์–ด ์ฒ˜๋ฆฌ

    • ํ—ค๋”: ํ‘œ์ค€ ์ธ์ฆ ํ—ค๋”๋กœ ์ธ์‹๋˜์–ด ๋ณด์•ˆ ๋ฏธ๋“ค์›จ์–ด์—์„œ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค
    • ๋ฐ”๋””: ์ผ๋ฐ˜ ๋ฐ์ดํ„ฐ๋กœ ์ทจ๊ธ‰๋˜์–ด ๋ณด์•ˆ ์ฒ˜๋ฆฌ๊ฐ€ ๋ˆ„๋ฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

    4. ๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ

    • ํ—ค๋”: ๋„คํŠธ์›Œํฌ ํƒญ์—์„œ ๋ณผ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, Authorization ํ—ค๋”์ž„์„ ๋ช…ํ™•ํžˆ ์ธ์‹ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
    • ๋ฐ”๋””: ์ผ๋ฐ˜ ๋ฐ์ดํ„ฐ์ฒ˜๋Ÿผ ๋ณด์—ฌ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ฏผ๊ฐํ•œ ์ •๋ณด์ž„์„ ๋†“์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

    ๊ถŒ์žฅ์‚ฌํ•ญ

    // โœ… ๊ถŒ์žฅ: Authorization ํ—ค๋” ์‚ฌ์šฉ
    fetch('/api/data', {
      headers: {
        'Authorization': 'Bearer ' + accessToken,
        'Content-Type': 'application/json'
      }
    });
    
    // โŒ ๋น„๊ถŒ์žฅ: ๋ฐ”๋””์— ํ† ํฐ ํฌํ•จ
    fetch('/api/data', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        accessToken: accessToken,
        otherData: data
      })
    });
    

    ๊ฒฐ๋ก : Authorization ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ณด์•ˆ์ƒ ๋” ์•ˆ์ „ํ•˜๋ฉฐ, HTTP ํ‘œ์ค€์„ ์ค€์ˆ˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๋ณด์•ˆ ๋„๊ตฌ์™€ ์ธํ”„๋ผ๊ฐ€ ์ด๋ฅผ ์ „์ œ๋กœ ์„ค๊ณ„๋˜์–ด ์žˆ์–ด ๋” ๋‚˜์€ ๋ณด์•ˆ ๋ณด์žฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

    Created Fri, 12 Sep 2025 08:25:18 +0900
  • 1) ์ด๋ฒคํŠธ ์ •์˜ (POJO/record ๊ถŒ์žฅ)

    // src/main/java/com/example/user/event/UserSignedUpEvent.java
    package com.example.user.event;
    
    import java.time.Instant;
    
    public record UserSignedUpEvent(Long userId, String email, Instant occurredAt) {}
    

    Spring 6+/Boot 3์—์„œ๋Š” ApplicationEvent๋ฅผ ์ƒ์†ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ํ‰๋ฒ”ํ•œ POJO/record๊ฐ€ ์ข‹์•„์š”.


    2) ์ด๋ฒคํŠธ ๋ฐœํ–‰ (raise)

    // src/main/java/com/example/user/service/SignUpService.java
    package com.example.user.service;
    
    import com.example.user.event.UserSignedUpEvent;
    import com.example.user.model.User;
    import com.example.user.repo.UserRepository;
    import org.springframework.context.ApplicationEventPublisher;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.time.Instant;
    
    @Service
    public class SignUpService {
    
        private final UserRepository userRepository;
        private final ApplicationEventPublisher publisher;
    
        public SignUpService(UserRepository userRepository, ApplicationEventPublisher publisher) {
            this.userRepository = userRepository;
            this.publisher = publisher;
        }
    
        @Transactional
        public Long signUp(String email, String name) {
            User user = userRepository.save(new User(email, name));  // JPA ์—”ํ‹ฐํ‹ฐ ์ €์žฅ ๊ฐ€์ •
            // ์ด๋ฒคํŠธ ๋ฐœํ–‰ (ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ)
            publisher.publishEvent(new UserSignedUpEvent(user.getId(), user.getEmail(), Instant.now()));
            return user.getId();
        }
    }
    

    3) ๋™๊ธฐ ๋ฆฌ์Šค๋„ˆ (๊ธฐ๋ณธ)

    // src/main/java/com/example/user/listener/AuditLogListener.java
    package com.example.user.listener;
    
    import com.example.user.event.UserSignedUpEvent;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.event.EventListener;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    @Component
    public class AuditLogListener {
        private static final Logger log = LoggerFactory.getLogger(AuditLogListener.class);
    
        @Order(0) // ์—ฌ๋Ÿฌ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์žˆ์„ ๋•Œ ์ˆœ์„œ ์ œ์–ด ๊ฐ€๋Šฅ (๋‚ฎ์„์ˆ˜๋ก ๋จผ์ €)
        @EventListener
        public void onUserSignedUp(UserSignedUpEvent event) {
            log.info("AUDIT: userId={}, email={}, at={}", event.userId(), event.email(), event.occurredAt());
        }
    }
    

    ๊ธฐ๋ณธ์€ โ€œ๋™๊ธฐ ์‹คํ–‰โ€์ด๋ผ ๋ฐœํ–‰ ์‹œ์ ์— ์ฆ‰์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

    Created Mon, 01 Sep 2025 16:13:53 +0900
  • Java Spring์—์„œ “reflection(๋ฆฌํ”Œ๋ ‰์…˜)“์€ Java์˜ ๋Ÿฐํƒ€์ž„ ์‹œ์ ์— ํด๋ž˜์Šค, ๋ฉ”์„œ๋“œ, ํ•„๋“œ ๋“ฑ์— ์ ‘๊ทผํ•˜๊ณ  ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์ปดํŒŒ์ผ ์‹œ์ ์ด ์•„๋‹Œ, ์‹คํ–‰ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์กฐ๋ฅผ ๋™์ ์œผ๋กœ ๋ถ„์„ํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.


    ๐Ÿ’ก ๋ฆฌํ”Œ๋ ‰์…˜์ด๋ž€?

    Java ๋ฆฌํ”Œ๋ ‰์…˜ API๋Š” java.lang.reflect ํŒจํ‚ค์ง€๋ฅผ ํ†ตํ•ด ์ œ๊ณต๋˜๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

    • ํด๋ž˜์Šค ์ด๋ฆ„์œผ๋กœ Class ๊ฐ์ฒด๋ฅผ ์–ป๊ธฐ
    • ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž, ๋ฉ”์„œ๋“œ, ํ•„๋“œ ์ •๋ณด ์กฐํšŒ
    • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ, ํ•„๋“œ ๊ฐ’ ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋“ฑ ๋™์  ์กฐ์ž‘
    • ์ ‘๊ทผ ์ œํ•œ์ž(private ๋“ฑ) ๋ฌด์‹œํ•˜๊ณ  ์ ‘๊ทผ (setAccessible(true))

    ๐Ÿ›  Spring์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์ด ์“ฐ์ด๋Š” ์ฃผ์š” ์˜ˆ

    Spring ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฆฌํ”Œ๋ ‰์…˜์„ ๋งค์šฐ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

    Created Tue, 29 Jul 2025 14:07:45 +0900
  • Spring Boot 3 + JPA + MySQL ํ™˜๊ฒฝ์—์„œ ํŠน์ • ํ…Œ์ด๋ธ”์— ๋Œ€ํ•ด find ๋ฐ save ์ž‘์—… ์‹œ row-level lock๋งŒ ์‚ฌ์šฉํ•˜๊ณ  table-level lock์„ ํ”ผํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ „๋žต์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


    โœ… ํ•ต์‹ฌ ๊ฐœ๋… ์š”์•ฝ

    • MySQL InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์€ ๊ธฐ๋ณธ์ ์œผ๋กœ row-level lock์„ ์ง€์›ํ•˜๋ฉฐ, JPA์˜ ๊ธฐ๋ณธ ๋™์ž‘์€ row-level lock์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    • ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์Œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ์•Š์œผ๋ฉด innoDB๋ผ๋„ table lock์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

      • WHERE ์ ˆ์— ์ธ๋ฑ์Šค๋ฅผ ์ž˜ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ฑฐ๋‚˜
      • ๋ฝ ๋ชจ๋“œ๊ฐ€ ๋ช…์‹œ๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜
      • UPDATE๋‚˜ SELECT ... FOR UPDATE์—์„œ ๋ฒ”์œ„ ๊ฒ€์ƒ‰์„ ์ œ๋Œ€๋กœ ๊ตฌ์„ฑํ•˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜

    โœ… ์ „์ œ ์กฐ๊ฑด: ํ…Œ์ด๋ธ”์ด InnoDB ์—”์ง„์ธ์ง€ ํ™•์ธ

    SHOW TABLE STATUS WHERE Name = 'your_table_name';
    
    • Engine ์ปฌ๋Ÿผ์ด InnoDB์—ฌ์•ผ row-level lock์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
    • ๋งŒ์•ฝ MyISAM์ด๋ฉด row-level lock์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ InnoDB๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:
    ALTER TABLE your_table_name ENGINE = InnoDB;
    

    โœ… JPA์—์„œ row-level locking๋งŒ ์ ์šฉํ•˜๊ธฐ

    1. @Lock(LockModeType.PESSIMISTIC_WRITE) ๋˜๋Š” @Lock(LockModeType.PESSIMISTIC_READ)

    @Repository
    public interface OrderRepository extends JpaRepository<Order, Long> {
    
        @Lock(LockModeType.PESSIMISTIC_WRITE)
        @Query("SELECT o FROM Order o WHERE o.id = :id")
        Optional<Order> findByIdForUpdate(@Param("id") Long id);
    }
    
    • PESSIMISTIC_WRITE๋Š” SELECT ... FOR UPDATE๋ฅผ ์‹คํ–‰ํ•˜์—ฌ row-level exclusive lock์„ ๊ฒ๋‹ˆ๋‹ค.
    • ์ด ์ฟผ๋ฆฌ๋Š” ํ•ด๋‹น row๋ฅผ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ณ€๊ฒฝํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋ง‰์Šต๋‹ˆ๋‹ค.
    • PESSIMISTIC_READ๋Š” ๊ณต์œ ๋ฝ์„ ๊ฑธ๋ฉฐ ์ฝ๊ธฐ๋Š” ํ—ˆ์šฉํ•˜๋˜ ์“ฐ๊ธฐ๋Š” ๋ง‰์Šต๋‹ˆ๋‹ค.

    โš ๏ธ ์ฃผ์˜: ์ด ๋ฐฉ๋ฒ•์€ ๋ฐ˜๋“œ์‹œ ํŠธ๋žœ์žญ์…˜(@Transactional) ์•ˆ์—์„œ ์‚ฌ์šฉ๋˜์–ด์•ผ ์‹ค์ œ๋กœ ๋ฝ์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

    Created Thu, 24 Jul 2025 10:07:45 +0900
  • ์•„๋ž˜์— Java Pathfinder (JPF) ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž์„ธํžˆ ์•ˆ๋‚ด๋“œ๋ฆฝ๋‹ˆ๋‹ค. Java Pathfinder๋Š” ์ž๋ฐ” ํ”„๋กœ๊ทธ๋žจ์˜ ๋ชจ๋ธ ์ฒดํ‚น(model checking) ๋„๊ตฌ๋กœ, ํ”„๋กœ๊ทธ๋žจ์˜ ์ƒํƒœ ๊ณต๊ฐ„์„ ํƒ์ƒ‰ํ•˜์—ฌ ๋ฒ„๊ทธ๋‚˜ ๋ฐ๋“œ๋ฝ ๋“ฑ์„ ์ฐพ๋Š” ๋ฐ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.

    ์•„๋ž˜ ๋‚ด์šฉ์„ ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…๋“œ๋ฆด๊ฒŒ์š”.


    ๐Ÿงญ 1. Java Pathfinder(JPF)๋ž€?

    **Java Pathfinder (JPF)**๋Š” ์ž๋ฐ” ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜๋ฉฐ ์ƒํƒœ๊ณต๊ฐ„์„ ์ฒด๊ณ„์ ์œผ๋กœ ํƒ์ƒ‰ํ•˜๋Š” ํˆด์ž…๋‹ˆ๋‹ค.

    ์ฃผ์š” ๊ธฐ๋Šฅ

    • ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๋ฒ„๊ทธ ํƒ์ง€
    • Assertion ์œ„๋ฐ˜ ๊ฒ€์ถœ
    • Deadlock ํƒ์ง€
    • Non-deterministic ๋™์ž‘ ๊ฒ€์ฆ
    • Symbolic execution (jpf-symbc) ์ง€์›

    โš™๏ธ 2. ์„ค์น˜

    JPF๋Š” GitHub ์ €์žฅ์†Œ์—์„œ ์ง์ ‘ ๋นŒ๋“œํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.

    Created Thu, 03 Jul 2025 10:49:24 +0900