Unit of Work ํŒจํ„ด

[์›๋ณธ ๋งํฌ]

Unit of Work(์ดํ•˜ UoW)๋Š” ์„œ๋น„์Šค ๋ ˆ์ด์–ด์™€ ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ ๋ ˆ์ด์–ด ์‚ฌ์ด์— ๋“ค์–ด๊ฐ€๋Š” ๋˜๋‹ค๋ฅธ ๋ ˆ์ด์–ด ํŒจํ„ด์ด๋‹ค.

์›น์„œ๋ฒ„์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ ˆ์ด์–ด ๊ตฌ์กฐ์— ๋Œ€ํ•ด ์ดํ•ดํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.




์›์ž์„ฑ (Atomic)

UoW์˜ ์ฃผ์š” ๋ชฉ์ ๊ณผ ์šฉ๋„๋Š” I/O ์ž‘์—…์— ๋Œ€ํ•œ ์›์ž์„ฑ์— ์žˆ๋‹ค.

์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, RDB์—์„œ ๋‹จ์ผ ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์—ฐ์‚ฐ์„ ๋ฌถ์–ด์„œ ์‚ฌ์šฉํ•  ๋•Œ๊ฐ€ ๋งŽ์„ ๊ฒƒ์ด๋‹ค.
ํŠนํžˆ ์ผ๋ถ€๋งŒ ์‹คํŒจํ•˜๋”๋ผ๋„ ๊ทธ ์ˆ˜์ •์‚ฌํ•ญ ์ „์ฒด๋ฅผ rollbackํ•ด์•ผ ํ•  ๊ฒฝ์šฐ ๋ง์ด๋‹ค.

UoW๋Š” ๊ทธ๋Ÿฐ ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„์˜ commit๊ณผ rollback์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ป๋ฐ๊ธฐ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ์“ฐ๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ฆผ์œผ๋กœ ํ‘œํ˜„ํ•˜๋ฉด ์ด๋ ‡๋‹ค.




์‚ฌ์šฉ๋ก€ (Node.js + Sequelize)

Nest.js + Sequelize์—์„œ UoW ํŒจํ„ด์„ ๊ตฌํ˜„ํ•ด์„œ ์ ์šฉํ•˜๋Š” ์˜ˆ์‹œ๋ฅผ ๋ณด์—ฌ๋ณด๊ฒ ๋‹ค.
UoW๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ ˆ์ด์–ด๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

๋ ˆํฌ์ง€ํ† ๋ฆฌ๊ฐ€ ์žˆ๊ณ , ์„œ๋น„์Šค๊ฐ€ ๋ฐ”๋กœ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๊ฐ–๋‹ค์“ด๋‹ค.

ํ•˜์ง€๋งŒ ์ด ์ฝ”๋“œ์—๋Š” ์›์ž์„ฑ์— ๋Œ€ํ•œ ๋ฐ˜์˜์ด ๋˜์–ด์žˆ์ง€ ์•Š๋‹ค.
์ € ์„œ๋น„์Šค์—์„œ ํ•˜๋‚˜๋ผ๋„ ์‚๋—ํ•œ๋‹ค๋ฉด ์–ด์ฉŒ๊ฒ ๋Š”๊ฐ€?

์ €๊ธฐ์„œ ๊ทธ๋ƒฅ ํŠธ๋žœ์žญ์…˜์„ ์ถ”๊ฐ€ํ•œ๋‹ค๋ฉด, ์ด๋Ÿฐ์‹์œผ๋กœ ํ•ด๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.
๋ ˆํฌ์ง€ํ† ๋ฆฌ ๋ฉ”์„œ๋“œ์—์„œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์ „๋‹ฌ๋ฐ›๊ณ ,

์„œ๋น„์Šค์—์„œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์ƒ์„ฑํ•ด์„œ ์ฃผ์ž…ํ•œ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ ๊ทธ๋Ÿญ์ €๋Ÿญ ์“ธ๋งŒํ•˜์ง€๋งŒ, ๊ทธ๋ฆฌ ์„ธ๋ จ๋œ ๋ชจ์–‘์ƒˆ๋Š” ์•„๋‹ˆ๋‹ค.
ํŠธ๋žœ์žญ์…˜ ์ž์ฒด๋Š” ์‚ฌ์‹ค ๋ฐ์ดํ„ฐ์™€ I/O์— ์ง์ ‘์ ์œผ๋กœ ์—ฐ๊ด€๋œ ๊ธฐ๋Šฅ์ธ๋ฐ, ๊ทธ๊ฑธ ์„œ๋น„์Šค์—์„œ ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ๋ง์ด๋‹ค.

๋ณด๋‹ค ๋ฐ”๋žŒ์งํ•œ ํ˜•ํƒœ๋Š” UoW ๋ ˆ์ด์–ด๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ํŠธ๋žœ์žญ์…˜์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ถ”์ƒํ™”ํ•˜๊ณ  ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
์ด๋Ÿฐ ์‹์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

์ปค๋ฐ‹๊ณผ ๋กค๋ฐฑ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑํ•ด์ฃผ๋Š” ์œ ํ‹ธ์„ฑ ๊ฐ์ฒด๊ฐ€ ๋๋‹ค.

๊ทธ๋ž˜์„œ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋Š” ํŠธ๋žœ์žญ์…˜ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ๋ฐ›์•„์•ผ ํ•˜๊ณ 

์„œ๋น„์Šค๋Š” ์ด์ œ UoW๋ฅผ ํ†ตํ•ด ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์•ก์„ธ์Šคํ•˜๊ฒŒ ๋œ๋‹ค.

์กฐ๊ธˆ ๋” ๊ฐ„๊ฒฐํ•ด์งˆ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ์—๋„ UnitOfWork์˜ Fake Object ๋ฒ„์ „์„ ๊ตฌํ˜„ํ•ด์„œ ๊ต์ฒดํ•˜๋ฉด ๋œ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

์•„๋ž˜๋Š” UoW์— ๋Œ€ํ•œ ๋ถ€๋ถ„์ฝ”๋“œ๋‹ค.

...
export interface IUnitOfWork {
  start(): void;
  complete(work: () => void): Promise<void>;
  getRepository<T>(R: new (transactionManager: any) => T): T;
}

@Injectable()
export class UnitOfWork implements IUnitOfWork {
  constructor(
    @Inject('SEQUELIZE')
    private sequelize: Sequelize,
  ) {}

  private transaction: Transaction | undefined;

  async setTransactionManager() {
    this.transaction = await this.sequelize.transaction();
  }

  async start() {
    await this.setTransactionManager();
  }

  getRepository<T>(R: new (transaction: Transaction) => T): T {
    if (!this.transaction) {
      throw new Error('Unit of work is not started. Call the start() method');
    }
    return new R(this.transaction);
  }

  async complete(work: () => void) {
    try {
      await work();
      await this.transaction?.commit();
    } catch (error) {
      await this.transaction?.rollback();
      throw error;
    }
  }
}
...



์ฐธ์กฐ
https://jideowosakin.com/unit-of-work-pattern-in-typescript/