Spring

[Spring+MongoDB]한 컬렉션에서 중복된 필드값 검증하기(feat. 고유 인덱스)

나맘임 2025. 2. 13. 05:18

들어가며

안녕하세요. 현재 "모아동"이라는 부경대학교 소속 동아리를 안내해 주는 서비스를 개발하고 있습니다.

Spring + MongoDB 기반으로 동아리 담당자 쪽 회원 가입 기능 개발에서 "한 컬렉션에서 중복된 필드값이 들어가는 문제"에 막혀 헤매다가 해결하게 되어 글을 쓰게 되었습니다.

문제 상황

public class User implements UserDetails {
	...
    
    @NotNull
    @Email
    @Size(min = 5, max = 50)
    private String email;
    
    ...
}

 

 

이메일 필드는 같은 컬렉션 내에서 중복이 되지 않기를 원했습니다.

 

해결 과정

단순히 구현한다면 find로 컬렉션 내에 이메일이 존재하는지 파악을 하고나서 있으면 save 없으면 에러 발생으로 해결할 수도 있습니다.

하지만 mysql의 unique처럼 DB 컬렉션 내에 중복된 데이터가 존재할 가능성을 만들고 싶지 않았습니다.

그러므로 DB에서 연산하기로 마음 먹었습니다.

GPT한테도 물어보고 구글에 검색도 해보고 그렇게 찾게 된 것이 인덱스(Index)입니다.

 

인덱스가 무엇인가?

이 데이터를 빠르게 찾아가기 위해 따로 관리되는 데이터 구조

 

데이터 구조?? 엥? 그러면 컬렉션과 무슨 차이일까요?

컬렉션은 데이터를 실제로 저장하고 있는 공간으로 JSON이나 BSON 형식 입니다.

JSON하고 BSON이라면 눈치 채실 수 있으실 텐데, 컬렉션은 특별한 구조가 없습니다.

이를 "스키마가 없다"라고도 말합니다.

[
  { "_id": 1, "name": "Alice", "age": 25 },
  { "_id": 2, "name": "Bob", "age": 30, "email": "bob@example.com" },
  { "_id": 3, "name": "Charlie", "active": true }
]

위와 같이 아무렇게나 막 데이터를 넣을 수 있습니다.

 

인덱스는 MongoDB 컬렉션의 데이터를 빠르게 찾기 위해 만들어지는 구조입니다.

MongoDB는 B-Tree 구조를 사용해 인덱스를 관리하며, 이는 데이터를 검색할 때 배열이나 리스트 같은 자료구조보다 훨씬 효율적입니다.
쉽게 말해, 인덱스는 데이터를 바로 찾을 수 있도록 미리 만들어둔 지도 같은 역할을 합니다.

 

예를 들어보겠습니다.

우리가 "id가 3인 문서를 찾아줘"라는 쿼리를 보낸다고 가정해 봅시다.

 

1️⃣ 먼저 인덱스에서 찾기 시작합니다.

  • MongoDB는 컬렉션을 전부 뒤지기 전에, 인덱스에서 id가 3인 값이 어디에 있는지 빠르게 찾아냅니다.
  • 인덱스가 id 3의 위치를 가리키고 있다면, 해당 위치를 통해 컬렉션에서 정확한 문서를 바로 가져옵니다.

2️⃣ 인덱스에 없다면?

  • 만약 인덱스에 id 3이 없다면, MongoDB는 어쩔 수 없이 컬렉션 전체를 뒤지며 문서를 하나하나 찾아야 합니다.
  • 이 과정은 훨씬 느리기 때문에, 인덱스를 만들어 두는 것이 검색 성능을 크게 향상시킵니다.

즉, 인덱스는 캐싱이라고 말할 수 있습니다.

 

이 인덱스를 중복 검증에 사용할 수 있습니다.

이걸 고유 인덱스라고 부릅니다.

중복된 값을 허용하지 않는 것이지요.

필연적으로 MongoDB는 인덱스를 만드니까 인덱스가 안만들어지는 조건을 건다면 오류를 검출할 수 있게 되는 겁니다.

 

MongoDB에서 고유 인덱스 적용하는 방법

db.컬렉션명.createIndex({필드명 : 1}, {unique: true})
//1 오름차순 -1 내림차순

MongoDB Shell에서 위와 같은 명령을 입력하시면 됩니다. 

 

인덱스는 기본적으로 DBMS를 조작해야 하는 작업입니다.

하지만, Spring의 JPA에 익숙해진 분들이라면 인덱스를 자바로 관리하고 싶으실 겁니다. 

 

그러면 어떻게 Java Spring에서 적용할 수 있을까요?

public class User implements UserDetails {
	...
    
    @NotNull
    @Email
    @Indexed(unique = true)
    @Size(min = 5, max = 50)
    private String email;
    
    ...
}

@Indexed(unique = true)을 사용하면 고유 인덱스를 넣을 수 있습니다.

여기까지만 하면 바로 적용이 되지 않습니다.

 

application.properties

spring.data.mongodb.auto-index-creation=true

자동으로 인덱스 생성을 허용시켜 줘야 원하는대로 작동할 겁니다.

 

주의사항

이미 컬렉션 안에 중복데이터가 있을 경우 고유 인덱스 설정 시 오류가 발생합니다.

데이터를 정리한 후에 넣어주세요.