๋ณธ ๊ธ€์€ ์ œ๊ฐ€ NestJS ํ”„๋ ˆ์ž„ ์›Œํฌ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ๊นจ๋‹ฌ์€ ๋…ธํ•˜์šฐ๋ฅผ ๊ธฐ๋กํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ œ์‹œํ•œ ๋ฐฉ๋ฒ•๋ณด๋‹ค ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€์ ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค :)

8 minute read

๋ณธ ๊ธ€์€ ์ œ๊ฐ€ NestJS ํ”„๋ ˆ์ž„ ์›Œํฌ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ๊นจ๋‹ฌ์€ ๋…ธํ•˜์šฐ๋ฅผ ๊ธฐ๋กํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ œ์‹œํ•œ ๋ฐฉ๋ฒ•๋ณด๋‹ค ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€์ ์€ ์–ธ์ œ๋‚˜ ํ™˜์˜์ž…๋‹ˆ๋‹ค :)

์ฃผ์˜!: ์ด ๊ธ€์€ MySQL DB์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.


NestJS ํ”„๋ ˆ์ž„์›Œํฌ๋Š” TypeORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด DB๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ๋ณธ ๊ธ€์€ TypeORM์„ ํ†ตํ•ด JSON ๋ฐฐ์—ด์„ MySQL DB์— ์–ด๋–ป๊ฒŒ ์ €์žฅํ• ์ง€์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ๋‹ค๋ฃฌ๋‹ค.

When we save โ€˜arrayโ€™ in DB column?

DB Column์— array ํƒ€์ž…์„ ์ €์žฅํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์ƒ๊ฐ๋ณด๋‹ค ์ž์ฃผ ๋“ฑ์žฅํ•œ๋‹ค.

Foreign Key Relation (OneToMany/ManyToOne)1

Person์— ๋Œ€ํ•œ Entity์™€ Email Entity๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž. ์ด๋•Œ, ํ•œ ์‚ฌ๋žŒ์ด ์—ฌ๋Ÿฌ ์ด๋ฉ”์ผ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ทธ ๋‘˜์˜ ๊ด€๊ณ„๋Š” โ€˜OneToMany/ManyToOneโ€™์ด๋‹ค. MySQL์˜ ํ…Œ์ด๋ธ” ์ƒ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ €์žฅ๋œ๋‹ค.

+----+--------------------+--------------+
| id | person             | email_id(fk) |
+----+--------------------+--------------+
| 1  | Tom                | 1            |
| 2  | Evans              | 2, 3         |
+----+--------------------+--------------+

+----+------------------+----------------+
| id | email            | person_id(fk)  |
+----+------------------+----------------+
| 1  | tom@mail.com     | 1              |
| 2  | evans@mail.com   | 2              |
| 3  | evans@univ.ac.kr | 2              |
+----+------------------+----------------+

๋‘ ํ…Œ์ด๋ธ”์„ ๋ณด๋ฉด, ๊ฐ๊ฐ email_id์™€ person_id๋ผ๋Š” foreign key๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. Person์˜ Evans ๊ฒฝ์šฐ ํŠน๋ณ„ํ•˜๊ฒŒ ๋‘ ๊ฐœ์˜ ์ด๋ฉ”์ผ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด email_id๋ฅผ ๋ฐฐ์—ด์„ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.

์ฆ‰, OneToMany/ManyToOne ๊ด€๊ณ„ ์•„๋ž˜์—์„œ fk_id Array๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋‹ค.

simple-array

Foreign key๊ฐ€ ์•„๋‹ˆ๋”๋ผ๋„, MySQL์˜ ์ปฌ๋Ÿผ์—๋Š” ๋ฐฐ์—ด์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค. TypeORM์€ simple-array๋ฅผ ํ†ตํ•ด์„œ MySQL์— ๊ฐ„๋‹จํ•œ ํ˜•ํƒœ์˜ ๋ฐฐ์—ด์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค€๋‹ค.

@Column('simple-array')
num_arr: number[];

@Column('simple-array')
str_arr: string[];

ํ•˜์ง€๋งŒ simple-array ๋ฐฉ์‹์œผ๋กœ string[]์„ ์ €์žฅํ•˜๊ฒŒ ๋˜๋ฉด, ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๊ฒƒ์€ ๋’ท๋ถ€๋ถ„์—์„œ ๋” ์ƒ์„ธํžˆ ๋‹ค๋ฃฌ๋‹ค.

How to save JSON array?

์ง€๊ธˆ๊นŒ์ง€ MySQL ์ƒ์—์„œ ๋ฐฐ์—ด์ด ์ €์žฅ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์‚ดํŽด๋ดค๋‹ค. ๊ทธ๋Ÿผ ์ด๋ฒˆ์—” JSON ๋ฐฐ์—ด์„ MySQL์— ์–ด๋–ป๊ฒŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ์„์ง€ ์‚ดํŽด๋ณด์ž.

simple-array๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž!

@Column('simple-array')
json_arr: JSON[];

@Column์— simple-array ์†์„ฑ์„ ์ฃผ๊ณ , ์ด์ „๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ์„ ์–ธํ•˜์˜€๋‹ค. ์ด ๊ฒฝ์šฐ๋Š” ๊ณผ์—ฐ ์ €์žฅํ•  ์ˆ˜ ์žˆ์„๊นŒ?? NOPE! ์ด ๊ฒฝ์šฐ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋‚œ๋‹ค. ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ดํŽด๋ณด์ž.

DataTypeNotSupportedError: Data type "Array" in "example.json_arr" is not supported by "mysql" database.

?? ์•ž์—์„œ ์šฐ๋ฆฌ๋Š” MySQL์ด ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์‚ดํŽด๋ดค๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ง€๊ธˆ์˜ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋Š” MySQL์—์„œ Array ํƒ€์ž…์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋งํ•œ๋‹ค.

์˜ค๋ฅ˜๋ฉ”์‹œ์ง€์˜ ๋ง์ด ๋งž๋‹ค. MySQL์€ Array ํƒ€์ž…์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์–ด๋–ค ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด์„œ ์šฐ๋ฆฌ์—๊ฒŒ ๋ฐฐ์—ด์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ ๊ฐ™์€ ๋Š๋‚Œ์„ ์ฃผ๋Š” ๊ฒƒ์ผ ๋ฟ์ด๋‹ค!!

simple-array๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์ž! - string[] ๋ฒ„์ „

์ง€๊ธˆ์˜ ๋ฌธ์ œ๋ฅผ ์šฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์ž. ์šฐ๋ฆฌ์˜ ๋ชฉ์ ์€ ์—ฌ์ „ํžˆ JSON ๋ฐฐ์—ด์„ MySQL์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ JSON[] ํ˜•ํƒœ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ˆ, ์ด์ „์— ์‚ดํŽด๋ณธ string[] ๋ฐฉ์‹์œผ๋กœ ์šฐํšŒํ•ด๋ณด์ž.

@Column('simple-array')
json_arr: string[];

javascript์—์„œ๋Š” JSON.stringify()๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด์„œ JSON์„ string ํ˜•ํƒœ๋กœ serialize ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๊ณ„ํš์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. API์—์„œ๋Š” JSON ๋ฐฐ์—ด๋กœ ์ž…๋ ฅ์„ ๋ฐ›๋Š”๋‹ค.
  2. ์ž…๋ ฅ๋ฐ›์€ JSON ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉฐ ๋ชจ๋“  JSON์— JSON.stringify()๋ฅผ ์ ์šฉํ•ด์„œ serialize
  3. MySQL์— string[]๋กœ ์ €์žฅ
  4. Get ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค string[]๋กœ ์ €์žฅ๋œ string ๋ฐฐ์—ด์˜ ๋ฌธ์ž์—ด์„ JSON.parse()๋กœ ํŒŒ์‹ฑํ•ด์„œ ๋ฆฌํ„ด

์ด๋ ‡๊ฒŒ ํ•œ๋‹ค๋ฉด JSON ๋ฐฐ์—ด์„ string ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์—†์ด DB์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒโ€ฆ

string[] ๋ฒ„์ „์˜ ๋ฌธ์ œ

JSON์„ ์ €์žฅํ•˜๋Š” ์šฐ๋ฆฌ์—๊ฒ ์ด ๋ฐฉ๋ฒ•๋„ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ JSON ๋ฐฐ์—ด์ด ์ €์žฅ๋œ๋‹ค๊ณ  ํ•ด๋ณด์ž.

[{name: 'Baker', job: 'musician'}, {name: 'Euler', job: 'mathematician'}]

์ด๊ฒƒ์„ JSON.stringify()๋กœ ๋ฐ”๊พธ๋ฉด,

["{name: 'Baker', job: 'musician'}", "{name: 'Euler', job: 'mathematician'}"]

๊ฐ€ ๋œ๋‹ค.

DB์— ์ €์žฅ๋„ ์ž˜ ๋œ๋‹ค. ํ•˜์ง€๋งŒ Get์œผ๋กœ DB์— ์ €์žฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•ด๋ณด๋ฉดโ€ฆ

["{name: 'Baker'",
 "job: 'musician'}",
 "{name: 'Euler'",
 "job: 'mathematician'}"]

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ดด์ƒํ•œ ํ˜•ํƒœ๊ฐ€ ๋œ๋‹คโ€ฆ!!

๊ทธ ์ด์œ ๋Š”โ€ฆ

MySQL์˜ pseudo-Array

์šฐ๋ฆฌ๋Š” MySQL์ด JSON ๋ฐฐ์—ด์€ ์ €์žฅ์ด ์•ˆ ๋˜๋”๋ผ๋„, number ๋ฐฐ์—ด์ด๋‚˜ string ๋ฐฐ์—ด ์ •๋„๋Š” ์ง€์›ํ•œ๋‹ค๊ณ  ์—ฌ๊ธฐ๊ณ  ์žˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒโ€ฆ ๊ทธ๊ฑด TypeORM์˜ ๋ˆˆ์†์ž„์ด์—ˆ๋‹ค!!

์‚ฌ์‹ค MySQL์€ Array ํƒ€์ž…์„ ์ „ํ˜€ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค!! (์ด๋ฏธ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋กœ ํ™•์ธ๋„ ํ–ˆ๋‹ค.) ๊ทธ๋Ÿผ ์šฐ๋ฆฌ๊ฐ€ simple-array ์†์„ฑ์„ ์จ์„œ ์ €์žฅํ•œ ๊ฑด ์–ด๋–ป๊ฒŒ ํ•œ ๊ฒƒ์ธ๊ฐ€??

simple-array๋Š” ๋ชจ๋“  ๋ฐฐ์—ด์„ string์œผ๋กœ ์ €์žฅํ•œ๋‹ค. ๋งŒ์•ฝ, number ๋ฐฐ์—ด์ด๋ผ๋ฉด, "1, 2, 3", string ๋ฐฐ์—ด์ด๋ผ๋ฉด, "Bill, John, Baker". ๊ทธ๋ž˜์„œ string ์ค‘๊ฐ„์— ์ฝค๋งˆ(,)๋ผ๋„ ๋“ค์–ด๊ฐ€๋ฉด, DB์—๋Š” ์ด๋ ‡๊ฒŒ ์ €์žฅ๋œ ๊ฒƒ์ด๋‹ค.

"'In other words, please be true', 'In other words, I love you'"

์ฆ‰, string ๋ฐฐ์—ด์ด๋”๋ผ๋„ DB์—๋Š” ๊ทธ๋ƒฅ ํ•˜๋‚˜์˜ string๋งŒ ์ €์žฅ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  TypeORM์€ ์ฝค๋งˆ(,)๋ฅผ ๊ธฐ์ค€์œผ๋กœ simple-array๋กœ ์„ ์–ธ๋œ Column์˜ ์ •๋ณด๋ฅผ ํŒŒ์‹ฑํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ์›๋ž˜ 2๊ฐœ์˜ string์„ ์ €์žฅํ–ˆ๋”๋ผ๋„, DB ๊ฐ’์„ ์ฝ์œผ๋ฉด 4๊ฐœ์˜ string์„ ์–ป๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค ใ… ใ… 

์‚ฌ์‹ค MySQL์€ ์ž˜๋ชป์ด ์—†๋‹ค. TypeORM์˜ simple-array ๋ฐฉ์‹์ด MySQL์—์„œ ์œ ๋… ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ๊ฒƒ์ผ ์ˆ˜๋„ ์žˆ๋‹ค. ๋‹ค๋ฅธ DB์—์„œ๋Š” ์ด๋ ‡๊ฒŒ ํ•˜๋‚˜์˜ string์œผ๋กœ ์ €์žฅ๋˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ๋‹ค.2 MySQL์—๋Š” ์ด๋ ‡๋‹ค๋Š” ๊ฒƒ์ด๋‹คโ€ฆ ใ… ใ… 

JSON์˜ ๊ฒฝ์šฐ Object๋ฅผ ๋‚˜์—ดํ•˜๊ธฐ ์œ„ํ•ด ์ฝค๋งˆ(,)๊ฐ€ ๋“ฑ์žฅํ•  ์ˆ˜ ๋ฐ–์— ์—†์ง€๋งŒ, ์ผ๋ฐ˜ string์—์„œ๋Š” ์ฝค๋งˆ๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋“œ๋ฌผ๋‹ค. ๊ทธ๋ž˜์„œ TypeORM ๊ฐœ๋ฐœ์ž๊ฐ€ ์•Œ์•„์ฑ„์ง€ ๋ชป ํ–ˆ์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๊ทธ์ทจ๋งŒ ์ด๋Ÿฐ ์‚ฌ์†Œํ•œ ์˜ค๋ฅ˜๋„ ๊ฐœ๋ฐœ์—์„œ๋Š” ํฐ ๊ฑธ๋ฆผ๋Œ์ด ๋˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๋‹ค. (์ด๊ฒƒ ๋•Œ๋ฌธ์— ๋ฐ˜๋‚˜์ ˆ์„ ๋‚ ๋ ธ๋‹คโ€ฆ)


[Solution] ๊ทธ๋ƒฅ string์œผ๋กœ ์ €์žฅํ•˜๋ผ!

๋ช‡๋ฒˆ์˜ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ํ†ตํ•ด TypeORM์˜ simple-array๊ฐ€ MySQL ์ƒ์—์„œ ์–ด๋–ป๊ฒŒ ๋ฐฐ์—ด์„ ์ €์žฅํ•˜๋Š”์ง€ ํ™•์ธํ•˜์˜€๋‹คโ€ฆ

๊ทธ๋Ÿผ ์ด๊ฑธ ํ™œ์šฉํ•ด์„œ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ๊ตฌ์„ฑํ•ด๋ณด์ž.

@Column()
json_arr: string;

Column์€ ๊ทธ๋ƒฅ string์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.

JSON ๋ฐฐ์—ด์˜ JSON ํ•˜๋‚˜ํ•˜๋‚˜๋Š” JSON.stringify() ํ•จ์ˆ˜๋กœ serialize ํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ๋“ค์„ JSON.parse()๊ฐ€ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋ณ€ํ˜•ํ•˜์—ฌ ์ €์žฅํ•œ๋‹ค.

// save JSON array into MySQL DB
saveJSONArray(json_arr: JSON[]) {
  let jsonArr_string = "[";
  for(let i=0; i < json_arr.length; i++){
    jsonArr_string += JSON.stringify(json_arr[i]);
    if(i < json_arr.length-1){
      jsonArr_string += ",";
    }
  }
  jsonArr_string += "]";
  this.yourRepository.save({ json_arr: jsonArr_string });
}

// load JSON array from MySQL DB
async findJSONArray(): Promise<YourEntry[]> {
  const entry_arr = await this.yourRepository.find();
  for(let i=0; i < entry_arr.length; i++){
    entry_arr[i].json_arr = JSON.parse(entry_arr[i].json_arr);
  }
  return entry_arr;
}

์ด๋ ‡๊ฒŒ ์•„์˜ˆ json_arr: string์œผ๋กœ ์„ค์ •ํ•˜๊ณ , DB์˜ ๊ฐ’์„ ์ฝ์„ ๋•Œ JSON.parse()๋กœ ํŒŒ์‹ฑํ•ด JSON์œผ๋กœ ๋ฆฌํ„ดํ•ด์ฃผ๋Š” ๋ฐฉ๋ฒ•์ด ์ตœ์„ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค. (๋ฌผ๋ก  ๋ˆ„๊ตฐ๊ฐ€๋Š” ์ด๊ฒƒ๋ณด๋‹ค ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„ ์ž˜ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์„ ์ˆ˜๋„ ์žˆ๋‹ค!)


  1. ManyToMany๋Š” OneToMany/ManyToOne์™€ ๋‹ค๋ฅด๊ฒŒ Array ๊ธฐ๋ฐ˜์ด ์•„๋‹ˆ๋‹ค. ManyToManyo์˜ ๊ฒฝ์šฐ function table์„ ๋งŒ๋“ค์–ด Relation์„ ๊ด€๋ฆฌํ•˜๊ณ  ๊ธฐ๋กํ•œ๋‹ค.ย 

  2. ๋ณธ์ธ์ด ์ง์ ‘ ํ™•์ธํ•ด๋ณธ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ, TypeORM์—์„œ ProgressDB์— ๋Œ€ํ•ด์„œ๋Š” jonb๋ฅผ ํ†ตํ•ด JSON ๋ฐฐ์—ด ์ €์žฅ ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ•œ ๊ฒƒ ๊ฐ™์•˜๋‹ค.ย 

Categories:

Updated: