์กฐ์šฉํžˆ ๋น„์–ด๋ฒ„๋ฆฐ ๊ฐ’ ํ•˜๋‚˜์—์„œ ์‹œ์ž‘ํ•œ ORM ๋ฉ”์ด์ € ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฒ€์ฆ ์ „๋žตNew

๐Ÿ“š
Backend
2026.09.18

๐Ÿ“š Overview

์ตœ๊ทผ ์‚ฌ๋‚ด ํŒจํ‚ค์ง€ ์—…๋ฐ์ดํŠธ์™€ ๋”๋ถˆ์–ด NestJS 12+ / MikroORM v7+ ๋กœ ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์€ ํƒ€์ž… ์—๋Ÿฌ๋ฅผ ์ง€์šฐ๊ณ  ๋ณ€๊ฒฝ๋œ API๋ฅผ ๋”ฐ๋ผ๊ฐ€๋Š”, ์˜ˆ์ƒ ๋ฒ”์œ„ ์•ˆ์˜ ์ž‘์—…์ด์—ˆ๋‹ค.

๊ทธ ๊ณผ์ •์—์„œ ์œ ๋… ์˜ค๋ž˜ ๋ถ™์žก๊ณ  ์žˆ์—ˆ๋˜ ๋ฌธ์ œ๊ฐ€ ํ•˜๋‚˜ ์žˆ์—ˆ๋‹ค. MikroORM ์—…๋ฐ์ดํŠธ ์ค‘, ์ž„๋ฒ ๋””๋“œ ๊ฐ’ ๊ฐ์ฒด(Value Object)๋กœ ๋งคํ•‘ํ•œ ํ•„๋“œ๊ฐ€ ํŠน์ • ์กฐํšŒ ๊ฒฝ๋กœ์—์„œ๋งŒ ์กฐ์šฉํžˆ ๋น„์–ด๋ฒ„๋ฆฌ๋Š” ํ˜„์ƒ์ด์—ˆ๋‹ค.

์—๋Ÿฌ๋„ ๋ถˆ๋ถ„๋ช…ํ–ˆ๊ณ , ํƒ€์ž… ์—๋Ÿฌ์กฐ์ฐจ ๋‚˜์ง€ ์•Š์•˜๋‹ค. ์ปดํŒŒ์ผ์€ ํ†ต๊ณผํ•˜๊ณ  ๋Ÿฐํƒ€์ž„์—์„œ๋งŒ ๊ฐ’์ด ์‚ฌ๋ผ์ง€๋Š” ํ˜•ํƒœ๋ผ, e2e ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์•„๋‹ˆ์—ˆ๋‹ค๋ฉด ์•„๋งˆ ์กฐ์šฉํžˆ ๋ฐฐํฌ๋˜์–ด ์šด์˜์—์„œ ๋งˆ์ฃผ์ณค์„ ๋ฌธ์ œ์˜€๋‹ค.

์ด์™€ ๋”๋ถˆ์–ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋”ฐ๋ผ์˜จ ์งˆ๋ฌธ์ด ์žˆ์—ˆ๋‹ค. ๋‹ค๋ฅธ ์ง€์ ์—์„œ๋„ ๊ฐ™์€ ์„ฑ๊ฒฉ์˜ ์ด์Šˆ๊ฐ€ ๋” ์žˆ์ง€ ์•Š์„๊นŒ?

๊ทธ๋ž˜์„œ e2e ์‹œ๋‚˜๋ฆฌ์˜ค์˜ ๊ฒ€์ฆ ์ถ•์„ ์˜ฎ๊ฒผ๋‹ค. ๋‹จ์ˆœํžˆ โ€œ์‘๋‹ต์ด ๊ฐ™์€๊ฐ€โ€๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์— ๋”๋ถˆ์–ด, โ€œ๋ฐœํ–‰๋˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ ๊ฐ™์€๊ฐ€โ€ ์— ํฌ์ปค์Šค๋ฅผ ๋งž์ถ”๋Š” ๋ฐฉํ–ฅ์œผ๋กœ.

์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ๊ทธ ํŒ๋‹จ์˜ ๊ณผ์ •๊ณผ, ๊ทธ๋ž˜์„œ ๋งŒ๋“ค๊ฒŒ ๋œ ์ฟผ๋ฆฌ ํ—ฌํผยท๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „์šฉ ํ…Œ์ŠคํŠธยทCI ์„ค๊ณ„๋ฅผ ๊ธฐ๋ก์œผ๋กœ ๋‚จ๊ธฐ๊ณ ์ž ํ•œ๋‹ค.



1. ์กฐ์šฉํžˆ ๋น„๋Š” ๊ฐ’ โ€” ์ž„๋ฒ ๋””๋“œ VO์™€ ์ปฌ๋Ÿผ ์ด๋ฆ„ ์ถฉ๋Œ


๋ฌธ์ œ๊ฐ€ ๋œ ๊ตฌ์กฐ๋Š” ์ด๋ ‡๋‹ค. ์‚ฌ์šฉ์ž ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๊ฐ’ ๊ฐ์ฒด๋กœ ๊ฐ์‹ธ ์ž„๋ฒ ๋””๋“œ๋กœ ๋งคํ•‘ํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

๊ฐ’ ๊ฐ์ฒด๋Š” ํ˜•์‹ ๊ฒ€์ฆ๊ณผ ํ•ด์‹ฑ ์ฑ…์ž„์„ ๊ฐ–๊ณ , ์‹ค์ œ ์ €์žฅ์€ ์ปฌ๋Ÿผ ํ•˜๋‚˜๋กœ ๋–จ์–ด์ง„๋‹ค.

// ๊ฐ’ ๊ฐ์ฒด ์ชฝ โ€” ์ž์‹ ์†์„ฑ value ๊ฐ€ ์‹ค์ œ ์ปฌ๋Ÿผ password ๋กœ ์ €์žฅ๋œ๋‹ค
export const PasswordSchema = new EntitySchema<Password>({
  class: Password,
  embeddable: true,
  properties: {
    value: {
      fieldName: 'password',
      type: 'string',
    },
  },
});

// ์‚ฌ์šฉ์ž ์—”ํ‹ฐํ‹ฐ ์ชฝ โ€” prefix ๋ฅผ ๋น„์›Œ ์ž์‹ ์ปฌ๋Ÿผ์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค
export const UserSchema = new EntitySchema<User>({
  class: User,
  properties: {
    // ...
    password: {
      kind: ReferenceKind.EMBEDDED,
      entity: () => Password,
      prefix: '',
    },
  },
});

User ์—”ํ‹ฐํ‹ฐ์— ์„ ์–ธํ•œ ์ž„๋ฒ ๋””๋“œ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„(password)๊ณผ, ๊ทธ ์•ˆ์˜ value ๊ฐ€ ์‹ค์ œ๋กœ ์ €์žฅ๋˜๋Š” ์ปฌ๋Ÿผ ์ด๋ฆ„(password)์ด ๊ฐ™๋‹ค๋Š” ์ ์ด ํ•ต์‹ฌ์ด๋‹ค.

v6์—์„œ๋Š” ์ด ๊ตฌ์„ฑ์ด ๋ฌธ์ œ์—†์ด ๋™์ž‘ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์˜ค๋žซ๋™์•ˆ ์•„๋ฌด๋„ ์˜์‹ฌํ•˜์ง€ ์•Š์•˜๋‹ค.

v7๋กœ ์˜ฌ๋ฆฐ ๋’ค, ์‚ฌ์šฉ์ž์™€ ๊ด€๊ณ„๋ฅผ ํ•จ๊ป˜ ์กฐํšŒํ•˜๋Š” ๊ฒฝ๋กœ์—์„œ๋งŒ ์‹คํŒจ๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

๊ด€๊ณ„๋ฅผ JOIN์œผ๋กœ ๋ถ™์—ฌ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋Š” ์ „๋žต์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์ปฌ๋Ÿผ๋งˆ๋‹ค ๋ณ„์นญ์ด ๋ถ™๋Š”๋‹ค.

select `e0`.*,
       `u1`.`user_id` as `u1__user_id`,
       `u1`.`password` as `u1__password`   -- ์ด ๋ณ„์นญ์ด ๋ฌธ์ œ์˜ ์ถœ๋ฐœ์ 
from `exchange_requests` as `e0`
inner join `users` as `u1` on `e0`.`user_id` = `u1`.`user_id`

๋ณ„์นญ์„ ๋‹ค์‹œ ํ”„๋กœํผํ‹ฐ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๋‹จ๊ณ„์—์„œ, ORM์€ password๋ผ๋Š” ์ด๋ฆ„์„ ์ปฌ๋Ÿผ๋ณด๋‹ค ํ”„๋กœํผํ‹ฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ ๋จผ์ € ์ฐพ๋Š”๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๊ทธ ์ด๋ฆ„์œผ๋กœ ๊ฐ€์žฅ ๋จผ์ € ๋งค์นญ๋˜๋Š” ๊ฒƒ์€ ์‹ค์ œ ๊ฐ’์„ ๋‹ด๋Š” ์ž์‹ ์ปฌ๋Ÿผ์ด ์•„๋‹ˆ๋ผ, Password ์ž„๋ฒ ๋”๋ธ”์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ”„๋กœํผํ‹ฐ ์ž์ฒด๋‹ค.

์ด ํ”„๋กœํผํ‹ฐ๋Š” ํ•˜๋‚˜์˜ ์ด๋ฆ„์ด ์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์œผ๋กœ ํŽผ์ณ์ง€๋Š” ๋ณตํ•ฉ ํ”„๋กœํผํ‹ฐ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋‹จ์ผ ์ปฌ๋Ÿผ์˜ ๊ฐ’์„ ๋ฐ›์•„์•ผ ํ•˜๋Š” ์ž๋ฆฌ์— ๋†“์ด๋Š” ์ˆœ๊ฐ„ ๋งคํ•‘์ด ์„ฑ๋ฆฝํ•˜์ง€ ์•Š๋Š”๋‹ค.

์›์ธ์„ ์ฐพ๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ ์ด์œ ๋Š” ์„ธ ๊ฐ€์ง€์˜€๋‹ค.

  • ํƒ€์ž… ๊ฒ€์‚ฌ๋กœ๋Š” ์ „ํ˜€ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค. ์Šคํ‚ค๋งˆ๋„ ์—”ํ‹ฐํ‹ฐ๋„ ์ •์ƒ์ ์ธ ํƒ€์ž…์ด๋‹ค.
  • User ์—”ํ‹ฐํ‹ฐ๋งŒ ๋‹จ๋…์œผ๋กœ ์กฐํšŒํ•  ๋•Œ๋Š” ์ •์ƒ์ด๋‹ค. ๋‹ค๋ฅธ ์—”ํ‹ฐํ‹ฐ์—์„œ User ๋ฅผ ํ•จ๊ป˜ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒฝ๋กœ์—์„œ๋งŒ ๊นจ์ง„๋‹ค.
  • ์‹คํŒจ ์ง€์ ์ด ๋„๋ฉ”์ธ๊ณผ ๋ฉ€๋‹ค. ์ฟผ๋ฆฌ ์กฐ๋ฆฝ ๋‚ด๋ถ€์—์„œ ํ„ฐ์ง€๋ฏ€๋กœ, ๋กœ๊ทธ๋งŒ ๋ณด๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ ๋งคํ•‘์ด ์›์ธ์ด๋ผ๋Š” ๋‹จ์„œ๊ฐ€ ์—†๋‹ค.

์ •๋ฆฌํ•˜๋ฉด โ€œ์—…๊ทธ๋ ˆ์ด๋“œ๋กœ ์ธํ•ด ๋Ÿฐํƒ€์ž„ ๊ทœ์น™์ด ๋ฐ”๋€Œ์—ˆ๋Š”๋ฐ, ๊ทธ ๋ณ€ํ™”๊ฐ€ ๊ฒ€์ฆ๋˜์ง€ ์•Š์€ ์ฑ„ ํŠน์ • ๊ฒฝ๋กœ์—์„œ๋งŒ ์กฐ์šฉํžˆ ๋“œ๋Ÿฌ๋‚˜๋Š”โ€ ํ˜•ํƒœ์˜€๋‹ค.

์ตœ์ข…์ ์œผ๋กœ ํƒํ•œ ํ•ด๋ฒ•์€ ์ด๋ฆ„ ์ถฉ๋Œ ์ž์ฒด๋ฅผ ์—†์• ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

// ์ž„๋ฒ ๋””๋“œ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„์„ ์ปฌ๋Ÿผ ์ด๋ฆ„๊ณผ ๋ถ„๋ฆฌํ•˜๊ณ , ํ”„๋กœํผํ‹ฐ๊ฐ€ ์ ์œ ํ•  ํ‚ค ์ด๋ฆ„๋„ fieldName ์œผ๋กœ ๋”ฐ๋กœ ์ง€์ •ํ•œ๋‹ค
hashedPassword: {
  kind: ReferenceKind.EMBEDDED,
  entity: () => Password,
  prefix: '',
  fieldName: '_password',
},

์ด ๋ฐฉ๋ฒ•์„ ๊ณ ๋ฅธ ์ด์œ ๋Š” DB ์Šคํ‚ค๋งˆ๋ฅผ ์ „ํ˜€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ปฌ๋Ÿผ ์ด๋ฆ„์€ ๊ทธ๋Œ€๋กœ password์ด๋ฏ€๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์ด ํ•„์š” ์—†๊ณ , ๊ตฌ๋ฒ„์ „ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ์˜ ๊ณต์กด๋„ ๊ทธ๋Œ€๋กœ ์œ ์ง€๋œ๋‹ค.

์ด ์ง€์ ์ด ์ค‘์š”ํ–ˆ๋˜ ๊ฑด, ๋กค๋ง ๋ฐฐํฌ ๊ณผ์ •์—์„œ ์Šคํ‚ค๋งˆ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ํ•จ๊ป˜ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๋ถ€๋‹ด์Šค๋Ÿฌ์› ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ปฌ๋Ÿผ ์ด๋ฆ„์„ ๋ฐ”๊พธ๋Š” ์„ ํƒ์„ ํ–ˆ๋‹ค๋ฉด ์‹ ๊ทœ ํ•„๋“œ ์ถ”๊ฐ€ โ†’ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ โ†’ ๊ตฌ/์‹  ํ•„๋“œ ๋ณ‘ํ–‰ ์กฐํšŒ(ํ•˜์œ„ ํ˜ธํ™˜) โ†’ deprecated ํ•„๋“œ ์ œ๊ฑฐ ๋ผ๋Š” ๋‹จ๊ณ„๋ฅผ ๋ฐŸ์•„์•ผ ํ•œ๋‹ค.

๋ฐฐํฌ ์ค‘ ๋‘ ๋ฒ„์ „์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ฐ™์€ ํ…Œ์ด๋ธ”์„ ๋™์‹œ์— ๋ฐ”๋ผ๋ณด๋Š” ๊ตฌ๊ฐ„์ด ์ƒ๊ธฐ๊ณ , ๊ฐ ๋‹จ๊ณ„๋งˆ๋‹ค ๋ณ„๋„์˜ ๋ฐฐํฌ์™€ ๊ฒ€์ฆ์ด ๋”ฐ๋ผ๋ถ™๋Š”๋‹ค. ์ด๋ฒˆ ๋ฌธ์ œ์˜ ํฌ๊ธฐ์— ๋น„ํ•ด ์ง€๋ถˆํ•ด์•ผ ํ•  ๋น„์šฉ์ด ์ง€๋‚˜์น˜๊ฒŒ ๋น„์‹ธ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.

๋ฐ˜๋ฉด ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„๋งŒ ๋ฐ”๊พธ๋Š” ์„ ํƒ์€ ๋ณ€๊ฒฝ์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ์•ˆ์—์„œ ๋๋‚œ๋‹ค. ๋ฐฐํฌ ์ˆœ์„œ์— ์ œ์•ฝ์ด ์—†๊ณ , ๋˜๋Œ๋ฆฌ๋Š” ๊ฒƒ๋„ ์ฝ”๋“œ ๋˜๋Œ๋ฆฌ๊ธฐ ํ•œ ๋ฒˆ์ด๋ฉด ๋œ๋‹ค.

๊ฒ€ํ† ํ–ˆ๋‹ค๊ฐ€ ์ ‘์€ ๋Œ€์•ˆ๋„ ๋‚จ๊ฒจ๋‘”๋‹ค.

  • ์ปค์Šคํ…€ ๋งคํ•‘ ํƒ€์ž…์œผ๋กœ ๊ต์ฒด: ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ ๋‹จ์ผ ์†์„ฑ ์ž„๋ฒ ๋”๋ธ” ๋Œ€์‹  ๊ถŒ๊ณ ํ•˜๋Š” ๋ฐฉํ–ฅ์ด๋‹ค(discussion #3685). ๊ตฌ์กฐ์ ์œผ๋กœ๋Š” ๊ฐ€์žฅ ๊นจ๋—ํ•˜์ง€๋งŒ, ๊ฐ’ ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ง„ ๊ฒ€์ฆยทํ•ด์‹ฑ ์ฑ…์ž„์„ ํƒ€์ž… ๋ณ€ํ™˜ ๊ณ„์ธต์œผ๋กœ ์˜ฎ๊ฒจ์•ผ ํ•ด์„œ ๋ณ€๊ฒฝ ๋ฒ”์œ„๊ฐ€ ์ปธ๋‹ค.
  • ์ „์—ญ ๋กœ๋”ฉ ์ „๋žต์„ ๋ณ„๋„ ์ฟผ๋ฆฌ ๋ฐฉ์‹์œผ๋กœ ๊ณ ์ •: JOIN ๋ณ„์นญ์„ ๋งŒ๋“ค์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ์šฐํšŒ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฟผ๋ฆฌ ๋นŒ๋”์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์กฐ์ธํ•ด ํ•จ๊ป˜ selectํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋‚จ์•„ ์žˆ์œผ๋ฉด ๊ทธ๋Œ€๋กœ ์‹คํŒจํ•œ๋‹ค. ๋ฌด์—‡๋ณด๋‹ค ๋ฌธ์ œ๋ฅผ ๋ฎ๋Š” ์„ ํƒ์ด์—ˆ๋‹ค.
  • ์ ‘๋‘์‚ฌ ์„ค์ •์„ ๋„๊ณ  ๊ฐ’ ๊ฐ์ฒด ์ชฝ์— ํ•„๋“œ๋ช…์„ ์ฃผ๋Š” ๋ฐฉ์‹: ๊ณผ๊ฑฐ์— ๋น„์Šทํ•œ ํšŒํ”ผ๋ฅผ ์‹œ๋„ํ•œ ์„ ๋ก€๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ, ๊ฐ’ ๊ฐ์ฒด์˜ ์บก์Аํ™”๋ฅผ ๊นจ๋œจ๋ฆฌ๋Š” ๋Œ€๊ฐ€๊ฐ€ ๋”ฐ๋ผ์™”๋‹ค.


2. ์‘๋‹ต ๋น„๊ต๋งŒ์œผ๋กœ๋Š” ์žกํžˆ์ง€ ์•Š๋Š” ๋ณ€ํ™” โ€” ๋ฐœํ–‰ ์ฟผ๋ฆฌ ์ˆ˜๋ฅผ ๊ณ„์•ฝ์œผ๋กœ ๋งŒ๋“ค๊ธฐ


์ด ์ด์Šˆ๋ฅผ ๋„˜๊ธฐ๊ณ  ๋‚˜๋‹ˆ ๋” ์‹ ๊ฒฝ ์“ฐ์ด๋Š” ๊ฒƒ์€ ๊ฐ™์€ ์„ฑ๊ฒฉ์˜ ๋ฌธ์ œ๊ฐ€ ๋˜ ์žˆ๋Š”๋ฐ ์•„์ง ์•ˆ ๋ณด์ด๋Š” ๊ฒƒ ์•„๋‹๊นŒ ํ•˜๋Š” ์ ์ด์—ˆ๋‹ค.

๋ฉ”์ด์ € ์—…๊ทธ๋ ˆ์ด๋“œ์—์„œ ์œ„ํ—˜ํ•œ ๋ณ€ํ™”๋Š” ๋Œ€์ฒด๋กœ ์ด๋Ÿฐ ๋ชจ์–‘์„ ํ•œ๋‹ค.

  • ์‘๋‹ต JSON์€ ์™„์ „ํžˆ ๋™์ผํ•˜๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ ๋‚ด๋ถ€์—์„œ ๋ฐœํ–‰๋˜๋Š” SQL์€ ๋‹ฌ๋ผ์กŒ๋‹ค.
  • ๊ทธ ์ฐจ์ด๊ฐ€ ์‹คํ–‰ ๊ณ„ํš์„ ๋ฐ”๊พธ๊ฑฐ๋‚˜, ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ๊ฐ’์ด ๋น„๊ฒŒ ๋งŒ๋“ ๋‹ค.

e2e ํ…Œ์ŠคํŠธ๋Š” ์‘๋‹ต์„ ๊ฒ€์ฆํ•˜๋ฏ€๋กœ ์ด ๋ณ€ํ™”๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ํ†ต๊ณผ์‹œํ‚จ๋‹ค. ์‹ค์ œ๋กœ ์ด๋ฒˆ ์—…๊ทธ๋ ˆ์ด๋“œ์—์„œ๋„ CI๋Š” ์ „๋ถ€ ์ดˆ๋ก์ด์—ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋ฐœํ–‰ ์ฟผ๋ฆฌ ์ž์ฒด๋ฅผ ๊ด€์ธก ๋Œ€์ƒ์œผ๋กœ ๋Œ์–ด์˜ฌ๋ฆฌ๋Š” ํ—ฌํผ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ์ปค๋„ฅ์…˜์ด SQL์„ ์‹คํ–‰ํ•˜๋Š” ๋‹จ์ผ ์ง€์ ์„ ๊ฐ์‹ธ ํ˜ธ์ถœ ํšŸ์ˆ˜๋ฅผ ์„ผ๋‹ค.

export function installQueryCounter(orm: MikroORM): QueryCounter {
  const connection = orm.em.getConnection();
  const original = connection.execute;
  let executed = 0;

  // ์›๋ณธ์˜ ๋ฐ˜ํ™˜๊ฐ’๊ณผ ์˜ˆ์™ธ๋ฅผ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผ์‹œํ‚จ๋‹ค โ€” ๋™์ž‘์„ ๋ฐ”๊พธ๋ฉด ์•ˆ ๋œ๋‹ค
  connection.execute = (...args) => {
    executed += 1;
    return original.apply(connection, args);
  };

  return {
    count: () => executed,
    reset: () => {
      executed = 0;
    },
    restore: () => {
      connection.execute = original;
    },
  };
}

์—ฌ๊ธฐ์„œ ํ•œ ๊ฐ€์ง€ ๊ฒฐ์ •์„ ํ–ˆ๋‹ค. SQL ๋ฌธ์ž์—ด์„ ๊ทธ๋Œ€๋กœ ๋‹จ์–ธํ•˜์ง€ ์•Š๋Š”๋‹ค.

๊ธฐ๋Œ€๊ฐ’์œผ๋กœ SQL์„ ๋ฐ•์•„๋‘๋ฉด ํ…Œ์ŠคํŠธ๋Š” ๊ฐ•๋ ฅํ•ด์ง€์ง€๋งŒ, ๋™์‹œ์— ๊ตฌํ˜„์„ ๊ณ ์ •ํ•ด๋ฒ„๋ ค ์žฌ์ž‘์„ฑ ์ž์ฒด๋ฅผ ๋ง‰๋Š”๋‹ค. ์—…๊ทธ๋ ˆ์ด๋“œ ๊ณผ์ •์—์„œ๋Š” ์ฟผ๋ฆฌ๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ธ๋ฐ, ๊ทธ ๋ชฉ์ ๊ณผ ์ •๋ฉด์œผ๋กœ ์ถฉ๋Œํ•œ๋‹ค.

๋Œ€์‹  ๊ณ„์•ฝ์„ ๋‘ ๊ฐ€์ง€๋กœ ์žก์•˜๋‹ค.

  1. ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ โ€” ๊ฐ™์€ ์กฐ๊ฑด์— ๊ฐ™์€ ๊ฒฐ๊ณผ, ๊ฐ™์€ ์ด ๊ฑด์ˆ˜.
  2. ๋ฐœํ–‰ ์ฟผ๋ฆฌ ์ˆ˜์˜ ์ƒํ•œ๊ณผ ํ•˜ํ•œ.
/**
 * ์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„ ๋ฐœํ–‰ ์ฟผ๋ฆฌ ์ˆ˜ ์ƒํ•œ.
 * ์—…๊ทธ๋ ˆ์ด๋“œ ์ด์ „ ๋ฒ„์ „์—์„œ ์‹ค์ œ๋กœ ์ธก์ •ํ•œ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๊ธฐ๋Œ€๊ฐ’์œผ๋กœ ์‚ผ๋Š”๋‹ค.
 * ์žฌ์ž‘์„ฑ ๊ฒฐ๊ณผ๊ฐ€ ์ด ๊ฐ’์„ ๋„˜์œผ๋ฉด N+1 ์ด ์ƒ๊ฒผ๊ฑฐ๋‚˜ ๋กœ๋”ฉ ์ „๋žต์ด ๋ฐ”๋€ ๊ฒƒ์ด๋‹ค.
 */
const QueryCountLimits = {
  // find ์˜ต์…˜์— populate ๋ฅผ ํ•จ๊ป˜ ๋„˜๊ฒจ ์—ฐ๊ด€์„ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ๋กœ
  inlinePopulate: 4,
  // ๋ฃจํŠธ๋ฅผ ๋จผ์ € ์กฐํšŒํ•œ ๋’ค em.populate() ๋กœ ์—ฐ๊ด€์„ ๋‚˜์ค‘์— ์ฑ„์šฐ๋Š” ๊ฒฝ๋กœ
  populateAfterFind: 4,
  // to-one ๊ด€๊ณ„๋งŒ ๋‘ ๋‹จ๊ณ„๋กœ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€๋Š” ์ค‘์ฒฉ ์กฐํšŒ ๊ฒฝ๋กœ
  nestedToOneOnly: 3,
  // to-one ๊ด€๊ณ„ ํ•˜๋‚˜๋งŒ ํ•จ๊ป˜ ๊ฐ€์ ธ์˜ค๋Š” ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ๊ฒฝ๋กœ
  toOneOnly: 2,
} as const;

/**
 * ์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„ ๋ฐœํ–‰ ์ฟผ๋ฆฌ ์ˆ˜ ํ•˜ํ•œ.
 * ๋ฃจํŠธ ์กฐํšŒ 1ํšŒ + populate ๊ฐ€ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€๋Š” ๊ณ„์ธต๋งˆ๋‹ค ์ตœ์†Œ 1ํšŒ๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ,
 * ์–ด๋–ค ์ „๋žต์„ ์“ฐ๋”๋ผ๋„ ์ด๋ณด๋‹ค ์ ๊ฒŒ ๋‚˜์˜ฌ ์ˆ˜๋Š” ์—†๋Š” ๊ฐ’์ด๋‹ค.
 * (์˜ˆ: toOneOnly ๋Š” JOIN ์œผ๋กœ ํ•ฉ์ณ๋„ ์ตœ์†Œ 1ํšŒ, ๋ณ„๋„ ์ฟผ๋ฆฌ๋ผ๋ฉด 2ํšŒ)
 *
 * ์ด ๊ฐ’์ด ํ•„์š”ํ•œ ์ด์œ ๋Š” ์ƒํ•œ ๋‹จ์–ธ๋งŒ์œผ๋กœ๋Š” "์ฟผ๋ฆฌ๊ฐ€ ๋„ˆ๋ฌด ์ ๊ฒŒ ๋‚˜์˜จ ๊ฒฝ์šฐ"๋ฅผ ์žก์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
 * ์นด์šดํ„ฐ๊ฐ€ SQL ๋ฐœํ–‰ ์ง€์ ์„ ๋†“์ณ 0 ์„ ์„ธ๋”๋ผ๋„ ์ƒํ•œ์€ ๋งŒ์กฑํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•ด๋ฒ„๋ฆฐ๋‹ค.
 */
const QueryCountFloors = {
  /* ... */
} as const;

ํ•˜ํ•œ์„ ํ•จ๊ป˜ ๋‘” ์ด์œ ๊ฐ€ ๊ฝค ์ค‘์š”ํ•˜๋‹ค. ๋ž˜ํผ๊ฐ€ ์–ด๋–ค ์‹คํ–‰ ๊ฒฝ๋กœ๋ฅผ ๋†“์ณ ์นด์šดํŠธ๊ฐ€ 0์ด ๋˜์–ด๋„, ์ƒํ•œ ๋‹จ์–ธ๋งŒ ์žˆ์œผ๋ฉด ํ…Œ์ŠคํŠธ๋Š” ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•œ๋‹ค. ์ด ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๊ฒ€์ฆํ•˜์ง€ ๋ชปํ•˜๋ฉด์„œ ํ†ต๊ณผ ์‹ ํ˜ธ๋งŒ ๋ณด๋‚ด๊ฒŒ ๋œ๋‹ค.

์ด ์ถ•์„ ์žก๊ณ  ๋‚˜๋‹ˆ v7์˜ ๊ธฐ๋ณธ ๋กœ๋”ฉ ์ „๋žต ๋ณ€๊ฒฝ(balanced)์ฒ˜๋Ÿผ ์ฟผ๋ฆฌ ๊ฐœ์ˆ˜ ์ž์ฒด๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ๋ณ€ํ™”๋„ ํ…Œ์ŠคํŠธ๊ฐ€ ๋งํ•ด์ฃผ๊ฒŒ ๋˜์—ˆ๋‹ค.



3. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „์šฉ ํ…Œ์ŠคํŠธ์™€, ๋–ผ์–ด๋‚ด๊ธฐ ์‰ฌ์šด CI ๊ตฌ์„ฑ


๋‹ค์Œ ๋ฌธ์ œ๋Š” โ€œ์ด ํ…Œ์ŠคํŠธ๋“ค์„ ์–ด๋””์— ๋‘˜ ๊ฒƒ์ธ๊ฐ€โ€์˜€๋‹ค.

์ด๋ฒˆ์— ์ถ”๊ฐ€ํ•œ ํ…Œ์ŠคํŠธ๋“ค์€ ์ฒ˜์Œ๋ถ€ํ„ฐ ํ•œ์‹œ์ ์ธ ์„ฑ๊ฒฉ์ด์—ˆ๋‹ค.

v6 ์—์„œ knex API ๋ฅผ ์ง์ ‘ ์“ฐ๊ณ  ์žˆ๋˜ ์ผ๋ถ€ ์ฟผ๋ฆฌ๋ฅผ ์ฟผ๋ฆฌ ๋นŒ๋” ๊ธฐ๋ฐ˜์œผ๋กœ ์žฌ์ž‘์„ฑํ•ด์•ผ ํ–ˆ๊ณ , ์ด ํ…Œ์ŠคํŠธ๋“ค์€ ๊ทธ ์žฌ์ž‘์„ฑ์ด ๊ธฐ์กด ๋™์ž‘์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ์žฅ์น˜์˜€๋‹ค.

๋ฐ”๊ฟ” ๋งํ•˜๋ฉด, ์žฌ์ž‘์„ฑ์ด ๋๋‚˜๊ณ  ๊ฒ€์ฆ๊นŒ์ง€ ๋งˆ์น˜๋ฉด ์—ญํ• ๋„ ํ•จ๊ป˜ ๋๋‚œ๋‹ค. ๊ทธ ์ดํ›„๋กœ๋Š” CI ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰ ์‹œ๊ฐ„๋งŒ ๋Š˜๋ฆด ๋ฟ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „์šฉ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ๊ทธ ์•„๋ž˜์—๋งŒ ๋ชจ์•˜๋‹ค. ๋””๋ ‰ํ„ฐ๋ฆฌ์งธ ์ง€์šฐ๋ฉด ํ…Œ์ŠคํŠธ๋„, CI ์„ค์ •๋„ ํ•จ๊ป˜ ์ •๋ฆฌ๋˜๋Š” ๊ตฌ์กฐ๋ฅผ ์›ํ–ˆ๋‹ค.

๊ณ„์•ฝ์˜ ์ถ•์ด HTTP ์‘๋‹ต์ด ์•„๋‹ˆ๋ผ ์ž…์ถœ๋ ฅ๊ณผ ๋ฐœํ–‰ ์ฟผ๋ฆฌ ์ˆ˜์˜€๊ธฐ ๋•Œ๋ฌธ์—, ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ์—์„œ ์ฟผ๋ฆฌ๋ฅผ ์ง์ ‘ ๋””์ŠคํŒจ์น˜ํ•˜๋„๋ก ํ–ˆ๋‹ค.

/**
 * ์ฟผ๋ฆฌ ์žฌ์ž‘์„ฑ ์ „ ๋™์ž‘์„ ๊ณ„์•ฝ์œผ๋กœ ๊ณ ์ •ํ•˜๋Š” ์ฟผ๋ฆฌ ์ „์šฉ ํ…Œ์ŠคํŠธ์šฉ ํ—ฌํผ.
 * ๊ณ„์•ฝ ์ถ•์ด SQL ๋ฌธ์ž์—ด์ด ์•„๋‹ˆ๋ผ ์ž…์ถœ๋ ฅ๊ณผ ๋ฐœํ–‰ ์ฟผ๋ฆฌ ์ˆ˜๋ผ, HTTP ๋Œ€์‹  ๋ฒ„์Šค๋กœ ์ง์ ‘ ๋””์ŠคํŒจ์น˜ํ•œ๋‹ค.
 */
export async function createQueryTestContext(): Promise<QueryTestContext> {
  const app = await createE2eApp();

  return { ...app, queryBus: app.get(QueryBus) };
}

CI์—์„œ๋Š” e2e ๋ฅผ ๋„๋ฉ”์ธ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ  ๋ณ‘๋ ฌ ์‹คํ–‰ํ•˜๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, ์ด ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ๋„๋ฉ”์ธ ๊ตฌ์กฐ ๋ฐ–์— ์žˆ์–ด ์ž๋™ ํƒ์ƒ‰์— ๊ฑธ๋ฆฌ์ง€ ์•Š์•˜๋‹ค.

๊ทธ๋ž˜์„œ ํƒ์ƒ‰ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์กฐ๊ธˆ ์†๋ดค๋‹ค. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์‹คํ–‰ ๊ทธ๋ฃน์„ ํ•˜๋‚˜ ๋ง๋ถ™์ด๊ณ , ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ง€์šฐ๋ฉด ๊ทธ ๊ทธ๋ฃน๋„ ๊ฐ™์ด ์‚ฌ๋ผ์ง€๊ฒŒ ํ–ˆ๋‹ค.

์—ฌ๊ธฐ์— ๊ฒ€์‚ฌ๋ฅผ ํ•˜๋‚˜ ๋” ๋„ฃ์—ˆ๋‹ค. ์–ด๋А ์‹คํ–‰ ๊ทธ๋ฃน์—๋„ ์žกํžˆ์ง€ ์•Š๋Š” e2e ํŒŒ์ผ์ด ์žˆ์œผ๋ฉด CI ๋ฅผ ์‹คํŒจ์‹œํ‚ค๋Š” ๊ฒƒ์ด๋‹ค. ๋กœ์ปฌ jest ๋Š” ํŒŒ์ผ ์œ„์น˜์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๊ธ์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์—, ์œ„์น˜๋ฅผ ์ž˜๋ชป ๋‘๋ฉด ๋กœ์ปฌ์—์„œ๋Š” ๋ฉ€์ฉกํžˆ ๋Œ๊ณ  CI ์—์„œ๋Š” ๊ทธ ํŒŒ์ผ์˜ ์กด์žฌ์กฐ์ฐจ ๋ชจ๋ฅธ ์ฑ„ ์กฐ์šฉํžˆ ์„ฑ๊ณตํ•œ๋‹ค. ์œ„์น˜๋ฅผ ์‚ฌ๋žŒ์ด ๋งค๋ฒˆ ๊ธฐ์–ตํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๊ฑธ๋Ÿฌ์ฃผ๋Š” ํŽธ์ด ๋‚ซ๋‹ค๊ณ  ๋ดค๋‹ค.

ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ค ์งœ๊ณ  ๋‚˜๋‹ˆ ํ•œ ๊ฐ€์ง€๊ฐ€ ๋” ๊ถ๊ธˆํ•ด์กŒ๋‹ค. ์ง€๊ธˆ ์ด ํ…Œ์ŠคํŠธ๊ฐ€ ๋ณด์žฅํ•˜๋Š” ๊ฑด ๊ฒฐ๊ตญ ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค ์•ˆ์—์„œ ์ฟผ๋ฆฌ๊ฐ€ ๊ฐ™๋‹ค๋Š” ๊ฒƒ๋ฟ์ธ๋ฐ, ์šด์˜์—์„œ ์‹ค์ œ๋กœ ๋‚˜๊ฐ€๋Š” ์ฟผ๋ฆฌ์™€ ๋น„๊ตํ•˜๋ฉด ์–ด๋–จ๊นŒ?

๋งˆ์นจ ์šด์˜ APM ์ด DB ์ŠคํŒฌ์— SQL ๋ณธ๋ฌธ์„ ๊ทธ๋Œ€๋กœ ๋‚จ๊ธฐ๊ณ  ์žˆ์—ˆ๋‹ค. ๋•๋ถ„์— ๋ฐฐํฌํ•˜์ง€ ์•Š๊ณ ๋„ ์šด์˜์ด ๋ฐœํ–‰ํ•œ ์ฟผ๋ฆฌ๋ฅผ ๊บผ๋‚ด๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๊ฐ™์€ API ๊ฒฝ๋กœ๋ฅผ ๋กœ์ปฌ์—์„œ ์ƒˆ ๋ฒ„์ „์œผ๋กœ ์‹คํ–‰ํ•ด SQL ์„ ๋ชจ์œผ๊ณ , ์–‘์ชฝ์„ ์ •๊ทœํ™”(๊ณต๋ฐฑ ์ถ•์•ฝ, ๋ฆฌํ„ฐ๋Ÿด์„ ์ž๋ฆฌํ‘œ์‹œ์ž๋กœ ์น˜ํ™˜)ํ•ด์„œ ๋น„๊ตํ–ˆ๋‹ค.

  • ๊ฐ€์žฅ ํฌ๊ฒŒ ์žฌ์ž‘์„ฑํ•œ ์ง‘๊ณ„ ์ฟผ๋ฆฌ: ์ •๊ทœํ™” ํ›„ 959์ž๊ฐ€ ์ „๋ถ€ ์ผ์น˜
  • UNION ํŒŒ์ƒ ํ…Œ์ด๋ธ”์„ ์“ฐ๋Š” ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ: ๊ตฌ์กฐ๋Š” ๊ฐ™๊ณ  ๊ฐ ๊ฐˆ๋ž˜๋ฅผ ๊ด„ํ˜ธ๋กœ ๊ฐ์‹ธ๋Š” ํ‘œ๊ธฐ๋งŒ ์ฐจ์ด
  • ๊ฐ’ ๊ฐ์ฒด ๋ฌธ์ œ๋ฅผ ๊ณ ์นœ ๋’ค์˜ JOIN ๋ณ„์นญ: ์šด์˜๊ณผ ์™„์ „ํžˆ ๋™์ผ


๐Ÿค” Understanding

์ด๋ฒˆ ์—…๊ทธ๋ ˆ์ด๋“œ์—์„œ ์–ป์€ ๊ตํ›ˆ์€, ๋ฉ”์ด์ € ๋ฒ„์ „ ๋ณ€๊ฒฝ์ด ํƒ€์ž…์œผ๋กœ ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š๋Š” ๋Ÿฐํƒ€์ž„ ๊ทœ์น™๊นŒ์ง€ ํ•จ๊ป˜ ๋ฐ”๊พผ๋‹ค๋Š” ์ ์ด๋‹ค.

๊ณต์‹ ์—…๊ทธ๋ ˆ์ด๋“œ ๊ฐ€์ด๋“œ์—๋„ ์กฐ์šฉํžˆ ๋™์ž‘์ด ๋‹ฌ๋ผ์ง€๋Š” ํ•ญ๋ชฉ๋“ค์ด ๋ถ„๋ช…ํžˆ ์ ํ˜€ ์žˆ๋‹ค.

Change detection is no longer automatic on scalar properties, an explicit em.persist call is required now

Both are now enabled by default, and the auto-fixing mechanism is removed.

Default loading strategy is balanced

์„ธ ์ค„ ๋ชจ๋‘ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์•Œ๋ ค์ฃผ์ง€ ์•Š๊ณ , ์‘๋‹ต๋งŒ ๋ณด๋Š” ํ…Œ์ŠคํŠธ๋„ ์•Œ๋ ค์ฃผ์ง€ ์•Š๋Š”๋‹ค. ๊ฒ€์ฆ์˜ ์ถ•์„ ๊ฒฐ๊ณผ์—์„œ ์ฟผ๋ฆฌ๋กœ ์˜ฎ๊ธด ๊ฒƒ์ด ์ด๋ฒˆ ์ž‘์—…์—์„œ ๊ฐ€์žฅ ๊ฐ’์–ด์น˜ ์žˆ๋Š” ๊ฒฐ์ •์ด์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ๋‹ค.

์ž„๋ฒ ๋””๋“œ ๊ฐ’ ๊ฐ์ฒด ์ด์•ผ๊ธฐ๋กœ ๋Œ์•„๊ฐ€๋ฉด, ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„๊ณผ ์ปฌ๋Ÿผ ์ด๋ฆ„์„ ๋งž์ถ”๋Š” ๋ฐฉ์‹์€ ์˜ค๋ž˜์ „๋ถ€ํ„ฐ ์ด์–ด์˜จ ํŒ€ ์ปจ๋ฒค์…˜์ด์—ˆ๋‹ค. v6 ์—์„œ๋Š” ๋ฌธ์ œ์—†์ด ๋™์ž‘ํ–ˆ์œผ๋‹ˆ ๋ˆ„๊ตฌ๋„ ์˜์‹ฌํ•  ์ด์œ ๊ฐ€ ์—†์—ˆ๊ณ , ๊ทธ ์ปจ๋ฒค์…˜์ด v7 ์—์„œ ์–ด๊ธ‹๋‚œ ๊ฒƒ ์ž์ฒด๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ฐ๋‹นํ•  ๋ชซ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

๋‹ค๋งŒ ํ•˜๋‚˜์˜ ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„์ด ์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์œผ๋กœ ํŽผ์ณ์ง€๋Š” ๊ตฌ์กฐ๊ฐ€ ๋‹จ์ผ ์ปฌ๋Ÿผ ์ž๋ฆฌ์— ๋†“์˜€๋‹ค๋ฉด, ORM ์ด ์ง„๋‹จ ๊ฐ€๋Šฅํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๋Š” ํŽธ์ด ๋งž๋‹ค๊ณ  ๋ณธ๋‹ค. ์ง€๊ธˆ์€ ์กฐํšŒ ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ๊ฐ’์ด ๋น„๋Š” ํ˜•ํƒœ๋กœ๋งŒ ๋“œ๋Ÿฌ๋‚˜์„œ ์›์ธ๊นŒ์ง€ ๋„๋‹ฌํ•˜๋Š” ๊ฑฐ๋ฆฌ๊ฐ€ ๋„ˆ๋ฌด ๋ฉ€์—ˆ๋‹ค.

๊ฐ™์€ ์‚ฌ๋ก€์˜ ์—…์ŠคํŠธ๋ฆผ ์ด์Šˆ๋Š” ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค. ์žฌํ˜„ ์กฐ๊ฑด์„ ์ขํžˆ๊ณ  โ€œ๋ฒ„๊ทธโ€๋กœ ๋ณผ์ง€ โ€œ์ง„๋‹จ ๋ฉ”์‹œ์ง€ ๊ฐœ์„  ์š”์ฒญโ€์œผ๋กœ ๋ณผ์ง€ ์Šค์Šค๋กœ ๋จผ์ € ๊ฒฐ๋ก ์„ ๋‚ธ ๋’ค, ์ตœ์†Œ ์žฌํ˜„ ์ €์žฅ์†Œ์™€ ํ•จ๊ป˜ ์ œ๋ณดํ•ด๋ณผ ์ƒ๊ฐ์ด๋‹ค. ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ ์ด๋ฏธ ๋‹จ์ผ ์†์„ฑ ์ž„๋ฒ ๋”๋ธ”๋ณด๋‹ค ์ปค์Šคํ…€ ํƒ€์ž…์„ ๊ถŒ๊ณ ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ, ์„ค๊ณ„๋ฅผ ๊ทธ๋Œ€๋กœ ๋‘” ์ฑ„ ๋™์ž‘๋งŒ ๋ฐ”๊ฟ”๋‹ฌ๋ผ๋Š” ์š”๊ตฌ๊ฐ€ ๋˜์ง€ ์•Š๋„๋ก ๋‹ค๋“ฌ๋Š” ์ผ์ด ๋จผ์ €๋‹ค.


์ฐธ๊ณ  ์ž๋ฃŒ

Previous
53๋ถ„์งœ๋ฆฌ ํฌ๋ก  ํ•˜๋‚˜์—์„œ ์‹œ์ž‘ํ•œ ๋ฐฐํฌ ์ข…๋ฃŒ ์„ค๊ณ„
๐Ÿ“šBackend
2026.09.08