ORM์ด๋ž€

Kjjeddยท2026๋…„ 1์›” 26์ผ

ORM

๋ชฉ๋ก ๋ณด๊ธฐ
5/8
post-thumbnail

๐Ÿงฉ ORM(Object-Relational Mapping)์ด๋ž€?

ORM์€ ๊ฐ์ฒด์ง€ํ–ฅ ์„ธ๊ณ„(ํด๋ž˜์Šค/๊ฐ์ฒด)์™€ ๊ด€๊ณ„ํ˜• DB ์„ธ๊ณ„(ํ…Œ์ด๋ธ”/ํ–‰)๋ฅผ ์ด์–ด์ฃผ๋Š” ํ†ต์—ญ ์‹œ์Šคํ…œ์ด๋‹ค. ๐ŸŒ‰
๊ฐœ๋ฐœ์ž๋Š” Python ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฃจ๋“ฏ์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ORM์€ ๊ทธ๊ฑธ SQL๋กœ ๋ฒˆ์—ญํ•ด์„œ DB์— ์š”์ฒญํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ๊ฐ์ฒด๋กœ ๋ณต์›ํ•œ๋‹ค.


๐Ÿง  ์™œ ORM์ด ํ•„์š”ํ• ๊นŒ?

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋ณดํ†ต ๊ฐ์ฒด๋กœ ์‚ฌ๊ณ ํ•œ๋‹ค.
User, Post, Comment ๊ฐ™์€ ๊ฒƒ๋“ค์„ ํด๋ž˜์Šค/๊ฐ์ฒด๋กœ ํ‘œํ˜„ํ•œ๋‹ค. ๐Ÿงฑ
๊ทธ๋Ÿฐ๋ฐ DB๋Š” ํ…Œ์ด๋ธ”/์ปฌ๋Ÿผ/ํ–‰์œผ๋กœ ์ €์žฅํ•œ๋‹ค. ์ฆ‰, ๋‘˜์€ ๊ธฐ๋ณธ ์ฒ ํ•™์ด ๋‹ค๋ฅด๋‹ค.

์ด ์ฐจ์ด๋ฅผ ํ”ํžˆ ๊ฐ์ฒด์ง€ํ–ฅ vs ๊ด€๊ณ„ํ˜• DB์˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ถˆ์ผ์น˜๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. (Object-Relational Impedance Mismatch) โšก

๊ฐ์ฒด์ง€ํ–ฅ ์„ธ๊ณ„                          ๊ด€๊ณ„ํ˜• DB ์„ธ๊ณ„
------------------                  ------------------
User ๊ฐ์ฒด (์†์„ฑ+ํ–‰๋™)                   users ํ…Œ์ด๋ธ” (๋ฐ์ดํ„ฐ)
user.email = "a@b.com"               UPDATE users SET email='a@b.com' ...
user.team.name                       SELECT ... JOIN teams ...

"์ (.)์œผ๋กœ ํƒ์ƒ‰"                        "JOIN์œผ๋กœ ์กฐ๋ฆฝ"

ORM์€ ์ด ๋ถˆ์ผ์น˜๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ–ˆ๋‹ค. ์ฆ‰, ๊ฐ์ฒด๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋˜ DB์™€์˜ ์—ฐ๊ฒฐ์€ ์ž๋™ํ™”ํ•˜๋Š” ๋„๊ตฌ๋‹ค. ๐Ÿ› ๏ธ


๐Ÿ†š ORM ์—†์ด(์ง์ ‘ SQL) vs ORM ์‚ฌ์šฉ

1) ORM ์—†์ด: SQL โ†’ ๊ฒฐ๊ณผ(ํŠœํ”Œ/๋”•์…”๋„ˆ๋ฆฌ) โ†’ ์ˆ˜๋™ ๋งคํ•‘

ORM ์—†์ด DB๋ฅผ ์“ฐ๋ฉด ๋ณดํ†ต ์ด๋Ÿฐ ํ๋ฆ„์ด ๋œ๋‹ค.

[ํ๋ฆ„]
1) SQL ๋ฌธ์ž์—ด ์ž‘์„ฑ
2) cursor.execute(...)
3) fetchone()/fetchall()
4) row๋ฅผ ๊ฐ์ฒด๋กœ ์ง์ ‘ ๋ณ€ํ™˜ (์ˆ˜๋™ ๋งคํ•‘)
5) ํ•„๋“œ ์ถ”๊ฐ€/๋ณ€๊ฒฝ ์‹œ SQL + ๋งคํ•‘ ์ฝ”๋“œ ํ•จ๊ป˜ ์ˆ˜์ •
# (์˜ˆ์‹œ) ์ง์ ‘ SQL + ์ˆ˜๋™ ๋งคํ•‘
import pymysql

conn = pymysql.connect(host="localhost", user="root", password="1234", db="mydb")
cursor = conn.cursor()

cursor.execute("""
    SELECT member_id, name, team_id
    FROM members
    WHERE member_id = %s
""", (1,))

row = cursor.fetchone()  # (1, "jay", 10) ๊ฐ™์€ ํ˜•ํƒœ

class Member:
    def __init__(self, member_id, name, team_id):
        self.member_id = member_id
        self.name = name
        self.team_id = team_id

member = Member(row[0], row[1], row[2])
print(member.name)

cursor.close()
conn.close()

๋ฌธ์ œ๋Š” ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๋‹ค. ๐Ÿ˜ตโ€๐Ÿ’ซ
ํ…Œ์ด๋ธ”์ด ๋Š˜๊ณ , ๊ด€๊ณ„๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด ๋งคํ•‘ ์ฝ”๋“œ๊ฐ€ ํญ๋ฐœํ•œ๋‹ค. ์ปฌ๋Ÿผ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ•ด๋„ SQL/๋งคํ•‘/ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ™์ด ํ”๋“ค๋ฆฐ๋‹ค.

์ƒํ™ฉ ORM ์—†์ด ์ƒ๊ธฐ๋Š” ์ผ
์ปฌ๋Ÿผ ์ถ”๊ฐ€/๋ณ€๊ฒฝ SQL ์ˆ˜์ • + ๋งคํ•‘ ์ฝ”๋“œ ์ˆ˜์ • + ๊ด€๋ จ ๋กœ์ง ์˜ํ–ฅ ๋ฒ”์œ„ ํ™•๋Œ€
JOIN ์ฆ๊ฐ€ SQL์ด ๊ธธ์–ด์ง€๊ณ  ์žฌ์‚ฌ์šฉ ์–ด๋ ค์›€, ์กฐํšŒ ๋ชฉ์ ๋งˆ๋‹ค ์ฟผ๋ฆฌ ํŒŒํŽธํ™”
๋„๋ฉ”์ธ ๋กœ์ง ํ™•์žฅ ๋น„์ฆˆ๋‹ˆ์Šค๋ณด๋‹ค DB ์—ฐ๋™ ์ฝ”๋“œ๊ฐ€ ๋” ๋งŽ์•„์ง€๋Š” ํ˜„์ƒ

2) ORM ์‚ฌ์šฉ: ํด๋ž˜์Šค ์ •์˜ โ†’ ORM์ด SQL ์ƒ์„ฑ/์‹คํ–‰ โ†’ ๊ฐ์ฒด๋กœ ๋ฐ˜ํ™˜

ORM์„ ์“ฐ๋ฉด ๊ฐœ๋ฐœ์ž๋Š” "ํ…Œ์ด๋ธ”" ๋Œ€์‹  "ํด๋ž˜์Šค"๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. ๐Ÿง‘โ€๐Ÿ’ป
ORM์€ ๋‚ด๋ถ€์—์„œ SQL์„ ๋งŒ๋“ค๊ณ  ์‹คํ–‰ํ•œ ๋’ค ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๋Œ๋ ค์ค€๋‹ค.

[ํ๋ฆ„]
1) ํด๋ž˜์Šค(๋ชจ๋ธ) ์ •์˜  = ํ…Œ์ด๋ธ” ๋งคํ•‘ ์ •๋ณด
2) session.get/query  = ์กฐํšŒ ์˜๋„๋งŒ ํ‘œํ˜„
3) ORM์ด SQL ์ƒ์„ฑ/์‹คํ–‰
4) ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๊ตฌ์„ฑํ•ด์„œ ๋ฐ˜ํ™˜
5) ์ˆ˜์ •์€ ๊ฐ์ฒด ๋ณ€๊ฒฝ โ†’ ORM์ด UPDATE๋กœ ๋ณ€ํ™˜
# (์˜ˆ์‹œ) SQLAlchemy ์Šคํƒ€์ผ ์˜์‚ฌ ์ฝ”๋“œ (๊ฐœ๋… ๋ณด์—ฌ์ฃผ๊ธฐ์šฉ)
# ์‹ค์ œ ์„ค์ •/์—”์ง„/์„ธ์…˜ ์ƒ์„ฑ์€ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค

class Team:
    id: int
    name: str

class Member:
    id: int
    name: str
    team: Team   # FK ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ผ "๊ฐ์ฒด ์ฐธ์กฐ"์ฒ˜๋Ÿผ ๋‹ค๋ฃฌ๋‹ค

member = session.get(Member, 1)         # PK๋กœ ์กฐํšŒ (ORM์ด SELECT๋ฅผ ๋งŒ๋“ ๋‹ค)
print(member.name)

print(member.team.name)                 # ๊ด€๊ณ„ ํƒ์ƒ‰ (ํ•„์š”ํ•˜๋ฉด JOIN/์ถ”๊ฐ€ ์ฟผ๋ฆฌ)

ํ•ต์‹ฌ์€ ์ด๊ฑฐ๋‹ค. ๐ŸŽฏ
SQL์„ "์ง์ ‘ ์ž‘์„ฑ"ํ•˜๋Š” ์‹œ๊ฐ„์ด ์ค„๊ณ , ๋Œ€์‹  ๊ฐ์ฒด ๋ชจ๋ธ๋ง๊ณผ ๋กœ์ง์— ์ง‘์ค‘ํ•˜๊ฒŒ ๋œ๋‹ค.


๐Ÿงพ ORM์ด ์‹ค์ œ๋กœ ํ•ด์ฃผ๋Š” ์ผ (ํ•ต์‹ฌ ๊ธฐ๋Šฅ 5๊ฐœ)

๊ธฐ๋Šฅ ๋ฌด์Šจ ์˜๋ฏธ์ธ๊ฐ€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฒด๊ฐํ•˜๋Š” ๋ณ€ํ™”
โœ… ๋งคํ•‘ ์ž๋™ํ™” ํ…Œ์ด๋ธ” โ†” ํด๋ž˜์Šค, ํ–‰ โ†” ๊ฐ์ฒด ๋ณ€ํ™˜ row[0], row[1] ๊ฐ™์€ ์ˆ˜๋™ ๋งคํ•‘ ์ œ๊ฑฐ
โœ… SQL ์ƒ์„ฑ ์กฐํšŒ/์ €์žฅ ์˜๋„๋ฅผ SQL๋กœ ๋ณ€ํ™˜ ๋ฐ˜๋ณต SQL ์ž‘์„ฑ ๊ฐ์†Œ, ์‹ค์ˆ˜ ๊ฐ์†Œ
โœ… ๊ด€๊ณ„ ๊ด€๋ฆฌ FK๋ฅผ ๊ฐ์ฒด ์ฐธ์กฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉ member.team.name ๊ฐ™์€ ํƒ์ƒ‰ ๊ฐ€๋Šฅ
โœ… ๋ณ€๊ฒฝ ๊ฐ์ง€ ๊ฐ์ฒด ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ถ”์  ๊ฐ’ ๋ฐ”๊พธ๋ฉด UPDATE ์ž๋™ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
โœ… ์บ์‹œ/์ผ๊ด€์„ฑ(Identity Map) ๊ฐ™์€ PK๋Š” ๊ฐ™์€ ๊ฐ์ฒด๋กœ ๊ด€๋ฆฌ(์„ธ์…˜ ๋‹จ์œ„) ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋กœ ๋‹ค๋ฃจ๋ฉฐ ๊ผฌ์ด๋Š” ๋ฌธ์ œ ๊ฐ์†Œ

๐Ÿงฉ ๋‹ค์ด์–ด๊ทธ๋žจ

[์ง์ ‘ SQL]
Python ์ฝ”๋“œ
  |
  |  1) SQL ๋ฌธ์ž์—ด ์ž‘์„ฑ
  v
DB Driver (pymysql ๋“ฑ)
  |
  |  2) SQL ์ „์†ก
  v
Database
  |
  |  3) row(ํŠœํ”Œ/๋”•์…”๋„ˆ๋ฆฌ) ๋ฐ˜ํ™˜
  v
Python ์ฝ”๋“œ
  |
  |  4) row โ†’ ๊ฐ์ฒด ์ˆ˜๋™ ๋ณ€ํ™˜
  v
๋„๋ฉ”์ธ ๊ฐ์ฒด ์‚ฌ์šฉ


[ORM]
Python ์ฝ”๋“œ (๊ฐ์ฒด ์ค‘์‹ฌ)
  |
  |  1) "์˜๋„"๋งŒ ํ‘œํ˜„(session.get / query)
  v
ORM (SQL ์ƒ์„ฑ + ๋งคํ•‘ + ๋ณ€๊ฒฝ ๊ฐ์ง€)
  |
  |  2) SQL ์ƒ์„ฑ/์‹คํ–‰
  v
Database
  |
  |  3) ๊ฒฐ๊ณผ ์ˆ˜์‹ 
  v
ORM
  |
  |  4) row โ†’ ๊ฐ์ฒด ์ž๋™ ๋ณ€ํ™˜
  v
๋„๋ฉ”์ธ ๊ฐ์ฒด ์‚ฌ์šฉ

โš ๏ธ ORM์„ ์“ฐ๋ฉด ๋ฌด์กฐ๊ฑด ์ข‹์€๊ฐ€?

์•„๋‹ˆ๋‹ค. ORM์€ ๋งŒ๋Šฅ์ด ์•„๋‹ˆ๋‹ค. ๋Œ€์‹  ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๊ฐ€ ์žˆ๋‹ค. ๐Ÿ”

1) N+1 ๋ฌธ์ œ

๊ด€๊ณ„๋ฅผ ์ (.)์œผ๋กœ ํŽธํ•˜๊ฒŒ ํƒ์ƒ‰ํ•˜๋‹ค๊ฐ€, ๋ฐ˜๋ณต๋ฌธ ์•ˆ์—์„œ ๋งค๋ฒˆ ์ถ”๊ฐ€ ์กฐํšŒ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ํญ๋ฐœํ•œ๋‹ค. ๐Ÿ’ฅ

[์˜ˆ์‹œ ์ƒํ™ฉ]
members 100๋ช… ์กฐํšŒ (1๋ฒˆ)
for member in members:
    member.team.name ์ ‘๊ทผ (ํŒ€์„ 100๋ฒˆ ์ถ”๊ฐ€ ์กฐํšŒ)

์ด 101๋ฒˆ ์ฟผ๋ฆฌ = 1 + 100  โ†’ ์ด๊ฒƒ์ด N+1์˜ ์ „ํ˜•์  ํ˜•ํƒœ

ํ•ด๊ฒฐ์€ ๋ณดํ†ต "ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๊ฒŒ" ๋งŒ๋“œ๋Š” ์ „๋žต(์กฐ์ธ ๋กœ๋”ฉ/ํ”„๋ฆฌํŒจ์น˜ ๋“ฑ)์„ ์„ ํƒํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•œ๋‹ค. ORM์„ ์“ฐ๋ฉด ์ด๋Ÿฐ ์„ฑ๋Šฅ ์ด์Šˆ๋ฅผ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๊ณ , ๋ฐ˜๋Œ€๋กœ ๋” ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

2) ๋ณต์žกํ•œ ๋ณด๊ณ ์„œ/ํ†ต๊ณ„ ์ฟผ๋ฆฌ

๋ณต์žกํ•œ GROUP BY, ์œˆ๋„์šฐ ํ•จ์ˆ˜, ๋Œ€์šฉ๋Ÿ‰ ํ†ต๊ณ„ ์ฟผ๋ฆฌ๋Š” ORM๋ณด๋‹ค SQL์ด ๋” ์ง๊ด€์ ์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ๐Ÿ“Š
์ด๋Ÿด ๋• ORM์„ ์“ฐ๋”๋ผ๋„ "Raw SQL"์„ ์„ž๋Š” ์„ ํƒ์„ ํ•˜๊ธฐ๋„ ํ•œ๋‹ค.


๐Ÿงช ORM์„ ์ž˜ ์“ฐ๋Š” ํ•ต์‹ฌ ๊ฐ๊ฐ

  • ๐Ÿ” ORM์€ SQL์„ ์ˆจ๊ธฐ๋Š” ๋„๊ตฌ๊ฐ€ ์•„๋‹ˆ๋ผ SQL์„ ์ƒ์„ฑํ•˜๋Š” ๋„๊ตฌ๋‹ค
  • ๐Ÿงพ ๊ทธ๋ž˜์„œ ORM์„ ์“ฐ๋”๋ผ๋„ ์ƒ์„ฑ๋˜๋Š” SQL์„ ๋ณด๋Š” ์Šต๊ด€์ด ์ค‘์š”ํ•˜๋‹ค
  • ๐Ÿšฆ ์„ฑ๋Šฅ ์ด์Šˆ๋Š” ๋Œ€๋ถ€๋ถ„ "์กฐํšŒ ์ „๋žต"์—์„œ ํ„ฐ์ง„๋‹ค (lazy/eager, join/prefetch)
  • ๐Ÿงฑ ๋„๋ฉ”์ธ ๋ชจ๋ธ(ํด๋ž˜์Šค ์„ค๊ณ„)์ด ์ž˜ ๋˜์–ด์•ผ ORM์˜ ์ด์ ์ด ๊ทน๋Œ€ํ™”๋œ๋‹ค

๐Ÿ“Œ ์š”์•ฝ

ํ‚ค์›Œ๋“œ ํ•ต์‹ฌ ๋ฌธ์žฅ
ORM ๊ฐ์ฒด์ง€ํ–ฅ ์ฝ”๋“œ์™€ ๊ด€๊ณ„ํ˜• DB ์‚ฌ์ด๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ํ†ต์—ญ ์‹œ์Šคํ…œ์ด๋‹ค
ORM ์—†์ด SQL ์ž‘์„ฑ + ๊ฒฐ๊ณผ ์ˆ˜๋™ ๋งคํ•‘์ด ๋ฐ˜๋ณต๋˜์–ด ์ฝ”๋“œ๊ฐ€ ๋น„๋Œ€ํ•ด์ง€๊ธฐ ์‰ฝ๋‹ค
ORM ์‚ฌ์šฉ ํด๋ž˜์Šค ์ค‘์‹ฌ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๊ณ  SQL ์ƒ์„ฑ/๋งคํ•‘/๋ณ€๊ฒฝ ๊ฐ์ง€๋ฅผ ์ž๋™ํ™”ํ•œ๋‹ค
์ฃผ์˜์  N+1 ๊ฐ™์€ ์กฐํšŒ ์ „๋žต ๋ฌธ์ œ๋ฅผ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค

๊ฒฐ๋ก โœจ
ORM์€ ๊ฐœ๋ฐœ์ž๊ฐ€ SQL์„ ๋œ ์“ฐ๊ฒŒ ํ•ด์ฃผ๋Š” ๋„๊ตฌ๊ฐ€ ์•„๋‹ˆ๋ผ, ๊ฐ์ฒด ์ค‘์‹ฌ์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ DB๋ฅผ ๋‹ค๋ฃจ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ตฌ์กฐ๋‹ค.

profile
Gongbuhaja

0๊ฐœ์˜ ๋Œ“๊ธ€