@EnableScheduling @SpringBootApplication public class RedisIntermediateProjectApplication { public static void main(String[] args) { SpringApplication.run(RedisIntermediateProjectApplication.class, args); } }
효율적인 실습을 위해 재고 차감 API를 미리 만들어뒀다.
@Entity(name = "stocks") @Getter @NoArgsConstructor public class Stock { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long quantity; public Stock(Long quantity) { this.quantity = quantity; } public void decrease(Long quantity) { if (this.quantity - quantity < 0) { throw new RuntimeException("재고는 0개 미만이 될 수 없습니다."); } this.quantity -= 1; } }
@RestController @RequestMapping("/stocks") @RequiredArgsConstructor public class StockController { private final StockService stockService; @PostMapping("/{id}/decrease") public void decrease( @PathVariable Long id ) { stockService.decrease(id); } }
@Service @RequiredArgsConstructor public class StockService { private final StockRepository stockRepository; @Transactional public void decrease(Long id) { Stock stock = stockRepository.findById(id).orElseThrow(); stock.decrease(id); stockRepository.save(stock); } }
public interface StockRepository extends JpaRepository<Stock, Long> { }



import http from 'k6/http'; import { check, sleep } from 'k6'; export const options = { // 가상 유저(VUs) 100명으로 설정 vus: 100, // 테스트를 10초 동안 진행 duration: '10s', }; export default function () { // 재고 차감 API const url = 'http://localhost:8080/stocks/1/decrease'; const params = { headers: { 'Content-Type': 'application/json', }, }; // POST 요청 전송 const res = http.post(url, null, params); // 1초 간격으로 요청 발송 sleep(1); // 응답 상태 코드 확인 (200 OK가 왔는 지 체크) check(res, { 'status is 200': (r) => r.status === 200, }); }
$ k6 run scripts/script_2-1.js

