T_era
Spring API와 Postman으로 네트워크 오버헤드 실험하기 본문
1. 실험 목적
API를 설계할 때 “오버헤드”라는 말을 자주 듣지만, 실제로 얼마나 영향을 미치는지 체감하기 어렵다.
이번 글에서는 Spring Boot API와 Postman을 이용해
- 단건 조회 N번 vs 전체 조회 1번
- 헤더 크기 변화
등이 네트워크 오버헤드에 어떤 영향을 주는지 직접 실험해보자.
2. 실험 환경 및 준비
2-1. 실험용 Spring 코드
Comment 엔티티
@Entity
@Getter
@Setter
@Table(name = "comments")
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String content;
public Comment(String content) {
this.content = content;
}
public Comment() {}
}
CommentService
@Service
public class CommentService {
private final CommentRepository commentRepository;
public CommentService(CommentRepository commentRepository) {
this.commentRepository = commentRepository;
}
public List<Comment> findAll() {
return commentRepository.findAll();
}
public Comment findById(Long id) {
return commentRepository.findById(id).orElse(null);
}
public void create() {
for(int i = 0; i < 100; i++) {
Comment comment = new Comment("test");
commentRepository.save(comment);
}
}
}
CommentController
@RestController
@RequestMapping("/api/comments")
@RequiredArgsConstructor
public class CommentController {
private final CommentService commentService;
@PostMapping("/createTest")
public ResponseEntity<Void> create() {
commentService.create();
return ResponseEntity.ok().build();
}
@GetMapping("/all")
public ResponseEntity<List<Comment>> getAllComments() {
List<Comment> comments = commentService.findAll();
return ResponseEntity.ok(comments);
}
@GetMapping("/{id}")
public ResponseEntity<Comment> getComment(@PathVariable Long id) {
Comment comment = commentService.findById(id);
return ResponseEntity.ok(comment);
}
@GetMapping("/test-header")
public ResponseEntity<String> testHeader(@RequestHeader Map<String, String> headers) {
int totalHeaderSize = headers.entrySet().stream()
.mapToInt(e -> e.getKey().length() + e.getValue().length())
.sum();
return ResponseEntity.ok("Total header size: " + totalHeaderSize + " bytes");
}
}
요청/응답 오버헤드 측정용 필터
@Component
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
int requestHeaderSize = Collections.list(req.getHeaderNames()).stream()
.mapToInt(name -> name.length() + req.getHeader(name).length())
.sum();
System.out.println("=== 요청 정보 ===");
System.out.println("URL: " + req.getRequestURL());
System.out.println("Method: " + req.getMethod());
System.out.println("Request Header Size: " + requestHeaderSize + " bytes");
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
int responseHeaderSize = res.getHeaderNames().stream()
.mapToInt(name -> name.length() + res.getHeader(name).length())
.sum();
System.out.println("=== 응답 정보 ===");
System.out.println("Status: " + res.getStatus());
System.out.println("Response Header Size: " + responseHeaderSize + " bytes");
System.out.println("Processing Time: " + (endTime - startTime) + "ms");
System.out.println("Total Overhead: " + (requestHeaderSize + responseHeaderSize) + " bytes");
System.out.println("========================\n");
}
}
3. 실험 방법
3-1. 데이터 준비
/api/comments/createTest엔드포인트로 100개의 테스트 댓글을 DB에 저장
3-2. Postman에서 실험
A. 전체 조회 1회
GET /api/comments/all- 한 번의 요청으로 100개의 댓글을 모두 조회
B. 단건 조회 100회
GET /api/comments/{id}- Postman Collection Runner로 id=1~100까지 100번 반복 호출
C. 헤더 오버헤드 실험
GET /api/comments/test-header- Authorization, Cookie 등 긴 헤더를 추가해서 요청
4. 실험 결과
4-1. 전체 조회 1회
- Postman
- Iterations: 1
- Duration: 683ms
- Avg. Resp. Time: 9ms
- Response Size: 3.04KB (Headers: 423B, Body: 2.63KB)
- Request Size: 295B (Headers: 295B)
- 서버 로그
=== 요청 정보 === URL: http://localhost:8081/api/comments/all Method: GET Request Header Size: 229 bytes Hibernate: select c1_0.id, c1_0.content from comments c1_0 === 응답 정보 === Status: 200 Response Header Size: 301 bytes Processing Time: 5ms Total Overhead: 530 bytes ========================
4-2. 단건 조회 1회
- 서버 로그
=== 요청 정보 === URL: http://localhost:8081/api/comments/1 Method: GET Request Header Size: 229 bytes Hibernate: select c1_0.id, c1_0.content from comments c1_0 where c1_0.id=? === 응답 정보 === Status: 200 Response Header Size: 301 bytes Processing Time: 30ms Total Overhead: 530 bytes ========================
4-3. 단건 조회 100회 (Postman Runner)
- Iterations: 100
- Duration: 1s 293ms
- Avg. Resp. Time: 3ms
4-4. 헤더 오버헤드 실험
- Postman
- Authorization, Cookie 등 긴 헤더 추가
- 응답:
Total header size: 459 bytes
- 서버 로그
=== 요청 정보 === URL: http://localhost:8081/api/comments/test-header Method: GET Request Header Size: 459 bytes === 응답 정보 === Status: 200 Response Header Size: 301 bytes Processing Time: 4ms Total Overhead: 760 bytes ========================
5. 결과 분석 및 인사이트
5-1. 전체 조회 1회 vs 단건 조회 100회
- 전체 조회 1회는 오버헤드(헤더 등)가 1번만 발생
- 단건 조회 100회는 오버헤드가 100번 누적 발생
- 530B x 100 = 53KB의 오버헤드가 발생
- 실제 데이터(Body)는 작지만, 네트워크 트래픽은 오히려 더 많아질 수 있음
실제 N+1 문제의 핵심은 DB 쿼리뿐 아니라 네트워크 오버헤드까지 누적된다는 점!
5-2. 헤더 오버헤드
- 불필요하게 긴 헤더(Authorization, Cookie 등)가 있으면
- Request Header Size, Total Overhead가 크게 증가
- 네트워크 효율이 떨어짐
5-3. 실무에서의 시사점
- API 설계 시
- 한 번에 필요한 데이터를 묶어서 보내는 것이 효율적
- 불필요한 헤더, 쿠키, 토큰은 최소화해야 함
- N+1 문제는 단순히 DB 쿼리만의 문제가 아니라,
네트워크 오버헤드까지 합쳐서 전체 시스템 성능에 큰 영향을 미침
6. 마치며
이번 실험을 통해
- 네트워크 오버헤드가 실제로 얼마나 누적될 수 있는지
- API 설계에서 오버헤드를 줄이는 것이 왜 중요한지
직접 체감할 수 있었다.
'Programing > Spring' 카테고리의 다른 글
| Spring Boot EC2 배포 & 테스트 과정에서 문제 해결 (0) | 2025.06.30 |
|---|---|
| 헬퍼(Helper)와 유틸(Utility)의 차이, 그리고 실전 코드 예시 (0) | 2025.06.25 |
| 템플릿 메서드 패턴과 AOP 로깅 구현: 패턴 적용의 적절성에 대한 고찰 (0) | 2025.06.23 |
| AOP로 로깅을 할 때 동적으로 값을 조율할 땐 어떤 방법을 사용할까 (1) | 2025.06.22 |
| WebFlux와 일반 route의 차이 (0) | 2025.06.16 |