Skip to content

๐ŸŽ‡ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

yewon edited this page Aug 13, 2025 · 9 revisions

1๏ธโƒฃ RDB & MongoDB ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ

image

๋ฌธ์ œ ์ƒํ™ฉ

๋ฌธ์„œ๋ฅผ ์‚ญ์ œํ•  ๋•Œ, RDB(MySQL)๊ณผ MongoDB์— ๋ถ„์‚ฐ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•จ๊ป˜ ์‚ญ์ œ๋˜์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์— ๋‹จ์ผ ํŠธ๋žœ์žญ์…˜์„ ๋ฌถ๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉฐ(ํ•˜๋‚˜์˜ @Transactional๋กœ ์ฒ˜๋ฆฌ X), ์ฒ˜๋ฆฌ ์ˆœ์„œ๋‚˜ ์‹คํŒจ ๋ณต๊ตฌ๋ฅผ ์ž˜ ์„ค๊ณ„ํ•˜์ง€ ์•Š์œผ๋ฉด ๋‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ„ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ˜„์žฌ ๊ตฌ์กฐ:

db ์ €์žฅ ๋‚ด์šฉ
RDB(JPA ๊ธฐ๋ฐ˜) Doc, Branch, Edge์™€ Save, Commit์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ (์ œ๋ชฉ, ์„ค๋ช…, ์ƒ์„ฑ/์ˆ˜์ • ์ผ์ž etc)
MongoDB SaveContent, Block, CommitsBlockSequence (Save์™€ Commit์˜ ๋ณธ๋ฌธ, Commit์˜ ๋ธ”๋ก ๋ฆฌ์ŠคํŠธ)

์ €์žฅ ํŠธ๋žœ์žญ์…˜ ์„ค๊ณ„

์ €์žฅ์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ„ํ—˜์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค:

  • Mongo ๋จผ์ € ์ €์žฅ โ†’ ์ดํ›„ RDB ์‹คํŒจ: Mongo์— ์“ฐ๋ ˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋‚จ์Œ
  • RDB ๋จผ์ € ์ €์žฅ โ†’ ์ดํ›„ Mongo ์‹คํŒจ: RDB์— ๋‚ด์šฉ์ด ์—†๋Š” ์ €์žฅ์ด ์ƒ๊น€

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ €์žฅ ๋กœ์ง์„ ๊ณ ์•ˆํ•˜์˜€๋‹ค:

โœ” MongoDB ์ €์žฅ ํ›„ โ†’ ์„ฑ๊ณต ์‹œ ID๋ฅผ ๋ฐ›์•„ โ†’ RDB์— ์ €์žฅ

์˜ˆ์‹œ ํ”„๋กœ์„ธ์Šค: RDB์˜ @Transactional ์•ˆ์—์„œ ๋‹ค์Œ ์ฒ˜๋ฆฌ ์‹คํ–‰

try {
    1. [RDB] MongoID๋ฅผ ์ œ์™ธํ•œ Save ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์šฐ์„  RDB์— ์ €์žฅ

    2. [MongoDB] SaveContent๋ฅผ MongoDB์— ์ €์žฅ โ†’ saveContentId ๋ฐ˜ํ™˜

    3. [RDB] ๋ฐ˜ํ™˜๋ฐ›์€ saveContentId๋ฅผ ๊ธฐ์กด Save(RDB)์— ์—…๋ฐ์ดํŠธ

    4. ์ตœ์ข…์ ์œผ๋กœ ๋ชจ๋“  ์ €์žฅ ์„ฑ๊ณต โ†’ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

} catch (์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ) {
    - [MongoDB ์ˆ˜๋™ ๋กค๋ฐฑ] ์‹คํŒจํ•œ Mongo ์ €์žฅ ๋‚ด์šฉ ์ง์ ‘ ์‚ญ์ œ (saveContentId ๋“ฑ)
    - ์˜ˆ์™ธ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์ ์ ˆํ•œ CustomException ๋ฐœ์ƒ ํ›„ 
    - ์ „์ฒด ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
}

์‚ญ์ œ ํŠธ๋žœ์žญ์…˜ ์„ค๊ณ„

์‚ญ์ œ์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ„ํ—˜์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค:

  • Mongo ์‚ญ์ œ ์ค‘ ์˜ˆ์™ธ โ†’ RDB๋Š” ์ด๋ฏธ ์‚ญ์ œ ์™„๋ฃŒ ์ƒํƒœ โ†’ ๋กค๋ฐฑ ๋ถˆ๊ฐ€๋กœ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๊นจ์ง

  • ๋ฐ˜๋Œ€๋กœ Mongo๋Š” ์‚ญ์ œ๋๋Š”๋ฐ RDB ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋œ๋‹ค๋ฉด โ†’ MongoDB์— ๊ณ ์•„ ๋ฐ์ดํ„ฐ ๋ฐœ์ƒ


์‚ญ์ œ๋Š” ์ €์žฅ๊ณผ ๋‹ฌ๋ฆฌ RDB๋งŒ ๋จผ์ € ์‚ญ์ œ๋˜๊ณ  MpongoDB์— ๊ณ ์•„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ๋”๋ผ๋„ UX ๊ด€์ ์—์„œ ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋ฏ€๋กœ, Mongo ์‚ญ์ œ๋ฅผ RDB ํŠธ๋žœ์žญ์…˜ ์ดํ›„๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‚ญ์ œ ๋กœ์ง์„ ๊ณ ์•ˆํ•˜์˜€๋‹ค:

โœ” ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ + ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ + ์žฌ์‹œ๋„ + ๋น„๋™๊ธฐ

  1. RDB ์‚ญ์ œ
  • Doc, Branch, Commit, Save ๋“ฑ(RDB)์€ ํ•˜๋‚˜์˜ @Transactional ๋‚ด์—์„œ ์‚ญ์ œ
  • ๋™์‹œ์— ์‚ญ์ œํ•ด์•ผํ•  Mongo ๊ด€๋ จ ID๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ DocDeleteMongoIdsDto์— ๋‹ด๊ณ 
  • eventPublisher.publishEvent()๋กœ ์‚ญ์ œ ์ด๋ฒคํŠธ ๋ฐœํ–‰
  1. MongoDB ์‚ญ์ œ
  • @TransactionalEventListener(phase = AFTER_COMMIT)๋ฅผ ํ†ตํ•ด RDB ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ปค๋ฐ‹๋œ ํ›„์—๋งŒ Mongo ์‚ญ์ œ๋ฅผ ์‹œ๋„
  • @Retryable์€ AOP ๊ธฐ๋ฐ˜์œผ๋กœ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ๋ผ์•ผ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒคํŠธ๋ฆฌ์Šค๋„ˆ์—์„œ ์ง์ ‘ ์‚ญ์ œํ•˜์ง€ ์•Š์Œ (๋ฆฌ์Šค๋„ˆ์—์„œ ์ง์ ‘ ํ˜ธ์ถœ ์‹œ ๋™์ž‘ ์•ˆ ํ•จ)
  1. Mongo ์‚ญ์ œ: ์žฌ์‹œ๋„
  • MongoDB๋Š” Atlas ๊ธฐ์ค€ ๊ธฐ๋ณธ์ ์œผ๋กœ Replica Set์„ ์ง€์›ํ•˜๋ฏ€๋กœ, ํŠธ๋žœ์žญ์…˜ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•จ
  • MongoTransactionManager๋ฅผ @Bean์œผ๋กœ ๋“ฑ๋กํ•ด ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ
  1. ์‚ญ์ œ ์‹คํŒจ โ†’ @Recover ์ง„์ž…
  • ์‚ญ์ œ๊ฐ€ 3ํšŒ ๋ชจ๋‘ ์‹คํŒจํ•˜๋ฉด @Recover ๋ฉ”์„œ๋“œ๋กœ ์ด๋™
  • ์ด ์‹œ์ ์—๋Š” ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ง์ ‘ RDB ์ €์žฅ ์‹œ๋„ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ (no transaction is in progress)
  • @Async ๋กœ ์œ„์ž„ํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์„ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ณ  Mongo ์‚ญ์ œ ์‹คํŒจ ๋‚ด์—ญ(MongoDeleteFailure)์„ ์ €์žฅ
  1. Mongo ์‚ญ์ œ ์‹คํŒจ ๊ธฐ๋ก ์ €์žฅ
  • @Async + @Transactional ์กฐํ•ฉ์œผ๋กœ ์ƒˆ ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ RDB์— Mongo ์‚ญ์ œ ์‹คํŒจ ๋‚ด์—ญ ์ €์žฅ
  1. ์‹คํŒจ ๋‚ด์—ญ ์žฌ์‹œ๋„: ์Šค์ผ€์ค„๋Ÿฌ ๊ธฐ๋ฐ˜
  • ์ผ์ • ์ฃผ๊ธฐ(1์‹œ๊ฐ„?)๋กœ MongoDeleteFailure ๋‚ด์—ญ ์กฐํšŒ
  • ์‚ญ์ œ ์„ฑ๊ณต ์‹œ resolved = true๋กœ soft delete ์ฒ˜๋ฆฌ


2๏ธโƒฃ ์—ฐ๊ด€๊ด€๊ณ„ CASCADE ์˜ค๋ฅ˜ ๋ฐ ํ•ด๊ฒฐ

๋ฌธ์ œ ์ƒํ™ฉ

๋ฌธ์„œ ์‚ญ์ œ์‹œ Edgde์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

Column 'next_commit_id' cannot be null

๋‹ค์Œ์€ ๊ธฐ์กด์˜ ๊ด€๋ จ ์ฝ”๋“œ์™€ ERD์ด๋‹ค.

  • ๋ฌธ์„œ(Doc) ์‚ญ์ œ ๋ฐฉ์‹
    • MongoDB: ์‚ญ์ œํ•  ๋ฌธ์„œ์™€ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ๋ฅผ dto๋กœ ๋งŒ๋“ค์–ด ์‚ญ์ œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰
    • RDB: cascade = CascadeType.ALL, orphanRemoval = true๋ฅผ ํ†ตํ•ด Doc์˜ ์‚ญ์ œ๋กœ ๊ด€๋ฆฌ
      • ์—๋Ÿฌ ์›์ธ ์˜ˆ์ƒ : Doc ์‚ญ์ œ์‹œ Edge๋ณด๋‹ค Commit์ด ๋จผ์ € ์ œ๊ฑฐ๋˜๋ฉฐ, Edge์˜ next_commit_id์—์„œ nullable = false ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐฐ
    public void delete(Long docId, Long userId) {
        User user = getUserOrThrow(userId);
        Doc doc = getDocByIdAndUserId(docId, userId);

        List<Branch> branches = doc.getBranches();
        MongoIdsDto docDeleteMongoIds = mongoIdsCollector.collectFrom(branches);

        user.removeDocument(doc);
        eventPublisher.publishEvent(docDeleteMongoIds);
  • Edgde ์—”ํ‹ฐํ‹ฐ
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "document_id", nullable = false)
    private Doc doc;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "prev_commit_id", nullable = false)
    private Commit prevCommit;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "next_commit_id", nullable = false)
    private Commit nextCommit;
  • Doc ์—”ํ‹ฐํ‹ฐ
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    @OneToMany(mappedBy = "doc", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private List<Branch> branches;

    @OneToMany(mappedBy = "doc", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private List<Edge> edges;
  • ERD
image

ํ•ด๊ฒฐ

  • DocService์—์„œ doc ์‚ญ์ œ์‹œ ๊ฐ„์„ ์„ ๋จผ์ € ๋ช…์‹œ์ ์œผ๋กœ ์‚ญ์ œํ•จ์œผ๋กœ์จ ํ•ด๊ฒฐํ•˜์˜€๋‹ค.
    public void delete(Long docId, Long userId) {
        User user = getUserOrThrow(userId);
        Doc doc = getDocByIdAndUserId(docId, userId);

        List<Edge> edges = doc.getEdges(); // ๊ฐ„์„  ์ˆ˜์ง‘
        List<Branch> branches = doc.getBranches();

        MongoIdsDto docDeleteMongoIds = mongoIdsCollector.collectFrom(branches);
        edgeRepository.deleteAll(edges); //๊ฐ„์„  ์‚ญ์ œ

        user.removeDocument(doc);
        eventPublisher.publishEvent(docDeleteMongoIds);

์›์ธ

์ด ์—๋Ÿฌ์˜ ์›์ธ์€ orphanRemoval ์— ์˜ํ•ด ๋ฐœ์ƒํ•œ๋‹ค.

  1. edges ์ปฌ๋ ‰์…˜์—์„œ Edge ๊ฐ์ฒด๊ฐ€ ์ œ๊ฑฐ๋จ
  2. Hibernate๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ Edge -> Commit ์ฐธ์กฐ๋ฅผ ๋Š์œผ๋ฉด์„œ(๋ฉ”๋ชจ๋ฆฌ์ƒ null ์„ค์ •) UPDATE๋ฅผ ๋‚ ๋ฆผ
  3. DB์—์„œ๋Š” nullable = false ์ œ์•ฝ ์กฐ๊ฑด ๋•Œ๋ฌธ์— UPDATE ์‹œ ์—๋Ÿฌ ๋ฐœ์ƒ.
  4. ๊ณง DELETEํ•  ๊ฐ์ฒด์ธ๋ฐ FK null ์ œ์•ฝ์ด ๋จผ์ € ๊ฑธ๋ ค์„œ ์‹คํŒจ.

์ฆ‰, DELETE ์ „ FK null UPDATE๊ฐ€ ๋ฐœ์ƒ

*์•„์ง ๋‹ค ์•ˆ ์”€

3๏ธโƒฃ MultipleBagException

Clone this wiki locally