200===Dev Language/Java

MultiValueMap - 하나의 키에 여러 값을 저장하는 자료구조 🗂️

블로글러 2025. 4. 1. 13:42

안녕하세요! 오늘은 프로그래밍에서 매우 유용하게 사용되는 자료구조인 MultiValueMap에 대해 알아보겠습니다. 일상에서 비유하자면, 일반 Map은 한 사람당 하나의 전화번호만 저장할 수 있는 전화번호부라면, MultiValueMap은 한 사람이 여러 개의 전화번호(집, 회사, 휴대폰 등)를 가질 수 있는 더 유연한 주소록이라고 생각하시면 됩니다! 😊

등장 배경

전통적인 Map 구조(HashMap, TreeMap 등)는 하나의 키에 단 하나의 값만 저장할 수 있습니다. 이는 대부분의 경우 충분하지만, 실제 애플리케이션 개발 시 하나의 키에 여러 값을 연결해야 하는 상황이 자주 발생합니다.

MultiValueMap이 해결하는 문제:

  1. HTTP 요청 파라미터 처리: 웹 요청에서 같은 이름의 파라미터가 여러 값을 가질 때
  2. HTTP 헤더 관리: 하나의 헤더 이름에 여러 값이 필요할 때
  3. 폼 데이터 처리: 동일한 필드에 여러 값이 입력될 때
  4. 그룹화된 데이터 관리: 카테고리별 아이템 목록 등을 관리할 때

핵심 원리

기본 구조

// Spring Framework의 MultiValueMap 인터페이스
public interface MultiValueMap<K, V> extends Map<K, List<V>> {
    // 주요 메서드들...
    V getFirst(K key);
    void add(K key, V value);
    void addAll(K key, List<V> values);
    void set(K key, V value);
    // ...
}

// 일반적인 사용 예
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("fruits", "apple");
map.add("fruits", "banana");
map.add("fruits", "orange");

// fruits 키에 대한 모든 값 가져오기
List<String> fruits = map.get("fruits"); // [apple, banana, orange]
// 첫 번째 값만 가져오기
String firstFruit = map.getFirst("fruits"); // apple

주요 구현체

  1. Spring Framework의 LinkedMultiValueMap: LinkedHashMap을 내부적으로 사용하며 ArrayList에 여러 값 저장
  2. Google Guava의 Multimap: ArrayListMultimap, HashMultimap, TreeMultimap 등 다양한 구현체 제공
  3. Apache Commons Collections의 MultiMap: 다양한 컬렉션 타입과 함께 사용 가능

사례 소개

1. HTTP 요청 파라미터 처리

Spring MVC에서는 HTTP 요청 파라미터를 처리할 때 MultiValueMap을 활용할 수 있습니다:

@GetMapping("/api/search")
public ResponseEntity<?> search(@RequestParam MultiValueMap<String, String> params) {
    // 여러 값을 가진 파라미터 처리
    List<String> categories = params.get("category"); // 여러 카테고리 값 처리
    // ...
    return ResponseEntity.ok().body(결과);
}

이렇게 하면 다음과 같은 URL 요청을 처리할 수 있습니다:
/api/search?category=electronics&category=books&category=clothes

2. HTTP 헤더 처리

HTTP 요청의 헤더 값은 종종 여러 개가 될 수 있습니다. 특히 Accept, Accept-Language와 같은 헤더는 여러 값을 가질 수 있습니다:

@GetMapping("/headers-example")
public ResponseEntity<?> handleHeaders(@RequestHeader MultiValueMap<String, String> headers) {
    List<String> acceptHeaders = headers.get("Accept");
    // 여러 Accept 헤더 값 처리
    return ResponseEntity.ok().body(acceptHeaders);
}

3. 폼 데이터와 멀티파트 요청 처리

파일 업로드나 복잡한 폼 데이터를 처리할 때 MultiValueMap은 매우 유용합니다:

// 멀티파트 요청 생성
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
parts.add("file", new FileSystemResource("document.pdf"));
parts.add("description", "중요 문서");
parts.add("tags", "중요");
parts.add("tags", "문서");

// RestTemplate으로 전송
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(parts, headers);
restTemplate.postForEntity("/upload", requestEntity, String.class);

주의사항 및 팁 💡

⚠️ 이것만은 주의하세요!

  1. 메모리 사용량: 많은 양의 데이터를 저장할 경우 메모리 사용량이 증가할 수 있습니다.

    • 필요할 때만 사용하고, 필요 없어지면 참조를 해제하세요.
    • 대용량 데이터는 적절한 캐싱 전략을 고려하세요.
  2. 성능 고려: 키에 해당하는 값이 많을 경우 검색 성능에 영향을 줄 수 있습니다.

    • 자주 접근하는 데이터는 별도 캐싱을 고려하세요.
    • 필요에 따라 더 효율적인 자료구조로 전환하세요.
  3. 값 타입 문제: 값들이 List로 저장되므로 특정 순서나 중복 처리 관련 이슈가 있을 수 있습니다.

    • 중복을 허용하지 않으려면 Set을 사용하는 다른 구현체를 고려하세요.
    • 순서가 중요하다면 LinkedMultiValueMap을 사용하세요.

💡 꿀팁

  • Spring의 LinkedMultiValueMap은 내부적으로 LinkedHashMap을 사용하므로 삽입 순서가 유지됩니다.
  • getFirst() 메서드를 활용해 첫 번째 값만 빠르게 접근할 수 있습니다.
  • 값 컬렉션이 비어있는지 확인할 때는 containsKey() 대신 containsValue()를 사용하는 것이 더 안전합니다.
  • HTTP 클라이언트 라이브러리를 사용할 때는 헤더와 파라미터를 위해 MultiValueMap을 활용하면 코드가 더 깔끔해집니다.

마치며

지금까지 MultiValueMap에 대해 알아보았습니다. 처음에는 단순한 Map의 확장으로 보일 수 있지만, 웹 개발과 데이터 처리에서 굉장히 유용한 자료구조입니다. 특히 Spring Framework를 사용하는 개발자라면 HTTP 요청 처리와 RESTful API 개발에서 자주 만나게 될 구조이니, 잘 이해해두시면 좋을 것 같습니다! 😄

여러분도 하나의 키에 여러 값을 저장해야 하는 상황이 생기면 Map<K, List>를 직접 구현하기보다는 이미 잘 구현된 MultiValueMap을 활용해보세요! 🚀


#MultiValueMap #Spring #자료구조 #자바 #웹개발

728x90