온라인 강의를 들으며 회원가입을 위한 컨트롤러에서 email
필드의 유효성을 검사해 이미 가입한 이메일인지 확인하는 부분을 구현하고 있었다.
구현 방법에 대해서 인덱스라는 개념을 언급하지만 다른 방법을 사용하겠다고 했다. 언급한 내용은 자신의 mongodb
강의에 있다며 판촉(?) 행위를 하길래 그냥 혼자 공부해야겠다 생각하고 Github Copilot
과 구글링을 통해 공부한 내용을 정리한다.
인덱스(INDEX), 고유 인덱스(UNIQUE INDEX), 고유 제약 조건(UNIQUE CONSTRAINT)에 대해 알아본다.
when using mongodb you could create an index in the mongo database on your email field and give that index the unique property.
방법에 대한 설명은 해주고 실제 강의에서는 주어진 이메일을 대조하는 방법을 사용했는데, 위 설명을 보면 email
필드에 index
를 생성하고 unique
속성을 부여하면 된다.
즉, email
필드를 UNIQUE INDEX
로 설정하면 된다.
이를 적용하면 무엇이 달라질지, 어떤 이점이 있을지 알아보도록 하겠다.
INDEX
?A
database index
is adata structure
thatimproves the speed of data retrieval operations
on a database table at the cost ofadditional writes
andstorage space
to maintain the index data structure.
데이터베이스의 인덱스(INDEX)는 검색 작업의 속도를 향상시키는 데이터 구조
데이터 구조는 해시 테이블(Hash Table)
과 B Tree
로 구현 가능하며 주로 B Tree
에서 파생된 구조로 구현됨 - B* Tree
, B+ Tree
인덱스 역시 데이터 구조이므로 추가적인 쓰기 작업과 저장 공간이 필요
INDEX
장단점인덱스를 사용하면 데이터 조회(SELECT
) 속도가 빨라진다.
WHERE
, ORDER BY
, MIN/MAX
속도가 빨라진다.
데이터베이스에서 특정 데이터를 조회(WHERE
)할 때 테이블 전체를 조회(full scan)해야 한다.
하지만 인덱스를 사용하는 경우 인덱스를 기준으로 정렬된 상태로 저장되어 있어 더 빠르게 찾을 수 있다.
정렬된 상태이므로 ORDER BY
, MIN/MAX
또한 빨라진다
인덱스라는 데이터 구조를 유지하기 위한 비용이 존재한다.
테이블과는 별개로 추가적인 저장 공간이 필요하고 인덱스가 적용된 열(Column)에 INSERT
, UPDATE
, DELETE
작업 시 정렬, 인덱스 구조 갱신 등 추가적인 연산이 발생한다.
조회(SELECT
)를 자주 하는 경우
삽입(INSERT
), 수정(UPDATE
), 삭제(DELETE
)가 자주 발생하지 않는 경우
데이터의 중복도가 낮은 경우
WHERE
, ORDER BY
, JOIN
을 자주 하는 경우
앞 내용들로 INDEX
에 대해서는 어느정도 알았으니 email
필드에 UNIQUE INDEX
를 부여해 중복 방지(unique) 및 빠른 조회(index)를 구현해보자.
models/user.js
const userSchema = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true, // unique 옵션 설정
},
});
Github Copilot Chat
답변
unique
옵션을 추가하면 해당 필드에 대한Unique Index
가 생성되며, 이를 통해 해당 필드를 기준으로 조회하는 작업이 더욱 빠르게 수행될 수 있습니다.mongoose
에서는unique
옵션을 가진 필드에 대해 자동으로 인덱스를 생성해주는 기능이 있으므로,unique
옵션을 추가하면mongoose
가 자동으로Unique Index
를 생성합니다
필드에 unique
옵션을 주면 mongoose
에서 자동으로 Unique Index
를 생성해준다. 이로서 email
필드에 대한 중복 방지 및 빠른 조회가 가능해졌다.
controllers/auth.js
postSignup: async (req, res, next) => {
const { name, email, password } = req.body;
try {
// email만 넣으면 됌!!
const existingUser = await User.findOne({ email });
...
} catch (err) {
...
}
},
email
필드가 중복값이 없음이 보장되고 index
가 적용되어있어 빠른 조회가 가능하다.
UNIQUE INDEX
적용 이유그렇다면 왜 email
필드에 unique
인덱스를 적용해야 하는 것일까?
INDEX
적용 이유email
필드에 대해 살펴보면 앞서 정리한 인덱스를 적용하기 적절한 경우에 대부분 해당한다.
조회(SELECT
)를 자주 하는 경우
이메일은 사용자의 데이터로서 게시글 작성자 확인 시 정보 표시 및 관리자 페이지에서 회원 조회 등 조회가 자주 일어날 수 있다.
삽입(INSERT
), 수정(UPDATE
), 삭제(DELETE
)가 자주 발생하지 않는 경우
사람마다 다르겠지만 특정 사이트에 가입 및 탈퇴를 자주 하지 않으며 가입한 이메일 수정 또한 빈번하지는 않다.
데이터의 중복도가 낮은 경우
이메일 또한 타 사이트에서 중복되지 않은 아이디로 가입해 만들어진 것으로 다른 사용자와 이메일 주소가 같을 가능성이 매우 적다.
"Unique" Index
인 이유email
필드는 중복값을 허용하지 않는 필드로 설정하고 싶었기 때문이다. 사용자들이 각자의 고유한 이메일 주소를 가지고 회원가입 및 로그인이 가능하도록 구현하려 했다.
Unique Constraint
와 Unique Index
Unique Constraint(고유 제약 조건)
는 내부적으로 Unique Index(고유 인덱스)
를 사용하여 구현된다. 고유 인덱스를 생성해 고유 제약 조건을 강제할 수 있기 때문이다.
두 기능을 지원하는 데이터베이스에서는 목적을 명시하는 목적으로 위 둘을 사용할 수 있다.
공통적으로 고유한 값을 가져 중복값을 허용하지 않는다는 조건이 적용된다고 간주한다.
필드를 검색, 정렬, 필터링하는 데 빠른 속도가 필요한 경우, 고유 인덱스를 명시적으로 사용하고 주석을 추가하여 코드에 명확하게 표시하면 된다.
이렇게 함으로써 나중에 고유성이 불필요해진 경우 인덱스 기능만 유지하면 되니 일반적인 non-unique 인덱스로 대체해야 함을 알 수 있다.
필드의 고유성을 강제하기 위해 인덱스가 필요하지 않고, 단순히 비즈니스 규칙으로 고유성을 검증해야 하는 경우, 고유 제약 조건을 사용하는 것이 좋다.
비즈니스 규칙에서 고유성을 강조할 때, 인덱스가 필요하지 않아도 내부적으로 UNIQUE INDEX를 이용해 구현하기 때문에 '인덱스가 필요하지 않지만 생긴다'는 사실을 인지해야 한다
IBM 문서 - Unique and non-unique indexes
When should I use a unique constraint instead of a unique index
Difference between unique indexes and unique constraints
클러스터드 인덱스 (Clustered Index), 넌 클러스터드 인덱스 (Non Clustered Index)