
데이터를 움직이는 힘 — ORM과 모델링 기초
서비스의 규모가 커질수록 코드와 데이터의 경계는 모호해집니다.
그 경계를 부드럽게 연결해주는 기술이 바로 ORM(Object Relational Mapping)입니다.
이번 글에서는 ORM의 개념과 Sequelize를 이용한 모델링 실습을 통해
“데이터를 코드처럼 다루는 법”을 배워봅니다.
ORM을 처음 접했을 때 느낀 혼란
처음 ORM을 접했을 때 가장 헷갈렸던 건,
“도대체 쿼리를 안 쓰고도 데이터를 어떻게 다루지?”였습니다.
SQL을 직접 작성하지 않아도 객체로 DB를 다룬다는 개념은 마치 마법 같았죠.
하지만 ORM은 단순한 편의 도구가 아니라, 객체지향과 데이터 설계의 접점입니다.
ORM(Object Relational Mapping)이란?
ORM은 객체(Object)와 관계형 데이터베이스(Relational DB) 사이를 연결하는 매핑 기술입니다.
개발자는 SQL을 직접 작성하지 않고, 클래스나 객체를 통해 데이터를 다룰 수 있습니다.
ORM이 필요한 이유
- SQL 대신 코드로 데이터 조작 → 생산성 향상
 - DBMS 교체 시 코드 최소 수정 → DB 독립성 확보
 - 비즈니스 로직과 데이터 로직의 분리
 
ORM의 한계
- 복잡한 SQL 쿼리는 ORM으로 표현하기 어렵다
 - 추상화 오버헤드로 인해 성능 저하 발생 가능
 - ORM 모델 구조를 잘못 설계하면 유지보수가 더 어려워짐
 
Sequelize로 이해하는 ORM 동작 원리
모델(Model)과 테이블(Table)의 매핑
ORM에서는 클래스가 테이블을, 클래스의 속성이 컬럼을 의미합니다.
// User 모델
const User = sequelize.define('User', {
  username: DataTypes.STRING,
  email: DataTypes.STRING,
});
→ 실제 DB에선 아래처럼 생성됩니다.
CREATE TABLE Users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(255),
  email VARCHAR(255)
);
인스턴스와 레코드의 관계
ORM의 인스턴스는 테이블의 한 행(row)과 같습니다.
user.save()는 INSERT 쿼리를, user.destroy()는 DELETE 쿼리를 실행합니다.
const user = await User.create({ username: 'jay', email: '[email protected]' });
await user.update({ email: '[email protected]' });
await user.destroy();
데이터 모델링의 기본 원칙
정규화(Normalization)의 3단계
- 1NF: 모든 컬럼은 원자값만 가져야 한다.
 - 2NF: 부분적 종속성을 제거한다.
 - 3NF: 이행적 종속성을 제거한다.
 
관계 설계 (1:1, 1:N, N:M)
- 1:1 → 사용자 ↔ 프로필
 - 1:N → 사용자 ↔ 게시글
 - N:M → 게시글 ↔ 태그 (중간 테이블 필요)
 
키(Key) 설계의 중요성
모든 테이블에는 반드시 기본키(PK)가 필요하며,
테이블 간 연결은 외래키(FK)로 관리합니다.
유니크 제약조건(Unique Key)으로 데이터 무결성을 강화하세요.
실습: Sequelize로 관계형 모델 설계하기
프로젝트 초기 세팅
npm install sequelize mysql2
모델 정의 (User, Post)
// models/User.js
const User = sequelize.define('User', {
  username: DataTypes.STRING,
  email: DataTypes.STRING
});
// models/Post.js
const Post = sequelize.define('Post', {
  title: DataTypes.STRING,
  content: DataTypes.TEXT
});
// 관계 설정
User.hasMany(Post, { foreignKey: 'userId' });
Post.belongsTo(User);
관계 기반 CRUD 실습
// 관계를 활용한 데이터 조회
const posts = await User.findOne({
  where: { id: 1 },
  include: Post
});
console.log(posts.Posts);
ORM 성능 최적화 팁
Lazy Loading vs Eager Loading
- Lazy Loading: 관계 데이터가 필요할 때만 쿼리 실행
 - Eager Loading: join으로 한 번에 로드 (include 옵션)
 
인덱스와 캐싱 전략
- 자주 조회되는 컬럼엔 인덱스 추가
 - 읽기 위주 API엔 Redis 캐시 적용
 - 쿼리 로그를 주기적으로 점검하여 N+1 문제 해결
 
FAQ: ORM 초보들이 자주 묻는 질문
Q1. ORM만으로 모든 쿼리를 처리할 수 있나요?
아니요. ORM이 처리하기 어려운 복잡한 SQL(집계, 조인 등)은 Raw Query로 처리할 수 있습니다.
Q2. Sequelize에서 관계가 꼬일 때는?
모델 정의 순서와 foreignKey 이름을 명시적으로 지정해보세요.
Q3. ORM을 쓰면 성능이 느려지지 않나요?
대부분의 CRUD 작업에서는 차이가 미미합니다. 다만 대량 조회·집계 쿼리는 Raw SQL이 유리합니다.
Q4. ORM을 쓰면 SQL을 몰라도 되나요?
SQL을 몰라도 개발은 가능하지만, ORM의 쿼리 동작을 이해하려면 기본 SQL 지식은 필수입니다.
Q5. Sequelize와 TypeORM의 차이는?
Sequelize는 유연성과 Node.js 친화적 문법, TypeORM은 TypeScript 기반의 강한 타입 안정성이 장점입니다.
Q6. 관계형 DB 말고 MongoDB도 ORM이 있나요?
네, Mongoose가 MongoDB용 ORM처럼 동작합니다. 다만 스키마 구조가 다르므로 동작 방식도 다릅니다.
마무리 — ORM은 데이터의 언어를 코드로 옮기는 기술
ORM은 데이터와 객체의 경계를 허물어, 개발자가 데이터를 코드처럼 다룰 수 있게 합니다.
하지만 ORM을 진짜 잘 쓰려면 데이터 구조, 관계, 성능까지 이해해야 합니다.
다음 편에서는 트랜잭션과 동시성 제어를 다루며, “데이터 일관성 유지”의 본질을 파헤치겠습니다.
참고자료:
Sequelize 공식 문서 ·
TypeORM ·
MDN Database Interaction
